@@ -73,7 +73,9 @@ pub struct Japanese {
7373/// These eras are loaded from data, requiring a data provider capable of providing [`CalendarJapaneseExtendedV1`]
7474/// data.
7575#[ derive( Clone , Debug , Default ) ]
76- pub struct JapaneseExtended ( Japanese ) ;
76+ pub struct JapaneseExtended {
77+ eras : DataPayload < CalendarJapaneseExtendedV1 > ,
78+ }
7779
7880impl Japanese {
7981 /// Creates a new [`Japanese`] using only modern eras (post-meiji) from compiled data.
@@ -116,11 +118,11 @@ impl JapaneseExtended {
116118 /// [📚 Help choosing a constructor](icu_provider::constructors)
117119 #[ cfg( feature = "compiled_data" ) ]
118120 pub const fn new ( ) -> Self {
119- Self ( Japanese {
121+ Self {
120122 eras : DataPayload :: from_static_ref (
121123 crate :: provider:: Baked :: SINGLETON_CALENDAR_JAPANESE_EXTENDED_V1 ,
122124 ) ,
123- } )
125+ }
124126 }
125127
126128 icu_provider:: gen_buffer_data_constructors!( ( ) -> error: DataError ,
@@ -135,9 +137,9 @@ impl JapaneseExtended {
135137 pub fn try_new_unstable < D : DataProvider < CalendarJapaneseExtendedV1 > + ?Sized > (
136138 provider : & D ,
137139 ) -> Result < Self , DataError > {
138- Ok ( Self ( Japanese {
139- eras : provider. load ( Default :: default ( ) ) ?. payload . cast ( ) ,
140- } ) )
140+ Ok ( Self {
141+ eras : provider. load ( Default :: default ( ) ) ?. payload ,
142+ } )
141143 }
142144}
143145
@@ -273,30 +275,136 @@ impl GregorianYears for &'_ Japanese {
273275 }
274276
275277 fn debug_name ( & self ) -> & ' static str {
276- if self . eras . get ( ) . dates_to_eras . len ( ) > 10 {
277- "Japanese (historical era data)"
278- } else {
279- "Japanese"
280- }
278+ "Japanese"
281279 }
282280
283281 fn calendar_algorithm ( & self ) -> Option < crate :: preferences:: CalendarAlgorithm > {
284- if self . eras . get ( ) . dates_to_eras . len ( ) > 10 {
285- None
286- } else {
287- Some ( crate :: preferences:: CalendarAlgorithm :: Japanese )
288- }
282+ Some ( crate :: preferences:: CalendarAlgorithm :: Japanese )
289283 }
290284}
291285
292286impl_with_abstract_gregorian ! ( Japanese , JapaneseDateInner , Japanese , this, this) ;
293287
288+ impl GregorianYears for & ' _ JapaneseExtended {
289+ fn extended_from_era_year (
290+ & self ,
291+ era : Option < & [ u8 ] > ,
292+ year : i32 ,
293+ ) -> Result < i32 , UnknownEraError > {
294+ if let Ok ( g) = CeBce . extended_from_era_year ( era, year) {
295+ return Ok ( g) ;
296+ }
297+ let Some ( era) = era else {
298+ // unreachable, handled by CeBce
299+ return Err ( UnknownEraError ) ;
300+ } ;
301+
302+ // Avoid linear search by trying well known eras
303+ if era == b"reiwa" {
304+ return Ok ( year - 1 + REIWA_START . year ) ;
305+ } else if era == b"heisei" {
306+ return Ok ( year - 1 + HEISEI_START . year ) ;
307+ } else if era == b"showa" {
308+ return Ok ( year - 1 + SHOWA_START . year ) ;
309+ } else if era == b"taisho" {
310+ return Ok ( year - 1 + TAISHO_START . year ) ;
311+ } else if era == b"meiji" {
312+ return Ok ( year - 1 + MEIJI_START . year ) ;
313+ }
314+
315+ let data = & self . eras . get ( ) . dates_to_eras ;
316+
317+ // Try to avoid linear search by binary searching for the year suffix
318+ if let Some ( start_year) = era
319+ . split ( |x| * x == b'-' )
320+ . nth ( 1 )
321+ . and_then ( |y| core:: str:: from_utf8 ( y) . ok ( ) ?. parse :: < i32 > ( ) . ok ( ) )
322+ {
323+ if let Ok ( index) = data. binary_search_by ( |( d, _) | d. year . cmp ( & start_year) ) {
324+ // There is a slight chance we hit the case where there are two eras in the same year
325+ // There are a couple of rare cases of this, but it's not worth writing a range-based binary search
326+ // to catch them since this is an optimization
327+ #[ expect( clippy:: unwrap_used) ] // binary search
328+ if data. get ( index) . unwrap ( ) . 1 . as_bytes ( ) == era {
329+ return Ok ( start_year + year - 1 ) ;
330+ }
331+ }
332+ }
333+
334+ // Avoidance didn't work. Let's find the era manually, searching back from the present
335+ let era_start = data
336+ . iter ( )
337+ . rev ( )
338+ . find_map ( |( s, e) | ( e. as_bytes ( ) == era) . then_some ( s) )
339+ . ok_or ( UnknownEraError ) ?;
340+ Ok ( era_start. year + year - 1 )
341+ }
342+
343+ fn era_year_from_extended ( & self , year : i32 , month : u8 , day : u8 ) -> types:: EraYear {
344+ let date: EraStartDate = EraStartDate { year, month, day } ;
345+
346+ let ( start, era) = if date >= MEIJI_START
347+ && self
348+ . eras
349+ . get ( )
350+ . dates_to_eras
351+ . last ( )
352+ . is_some_and ( |( _, e) | e == tinystr ! ( 16 , "reiwa" ) )
353+ {
354+ // We optimize for the five "modern" post-Meiji eras, which are stored in a smaller
355+ // array and also hardcoded. The hardcoded version is not used if data indicates the
356+ // presence of newer eras.
357+ if date >= REIWA_START {
358+ ( REIWA_START , tinystr ! ( 16 , "reiwa" ) )
359+ } else if date >= HEISEI_START {
360+ ( HEISEI_START , tinystr ! ( 16 , "heisei" ) )
361+ } else if date >= SHOWA_START {
362+ ( SHOWA_START , tinystr ! ( 16 , "showa" ) )
363+ } else if date >= TAISHO_START {
364+ ( TAISHO_START , tinystr ! ( 16 , "taisho" ) )
365+ } else {
366+ ( MEIJI_START , tinystr ! ( 16 , "meiji" ) )
367+ }
368+ } else {
369+ let data = & self . eras . get ( ) . dates_to_eras ;
370+ #[ allow( clippy:: unwrap_used) ] // binary search
371+ match data. binary_search_by ( |( d, _) | d. cmp ( & date) ) {
372+ Err ( 0 ) => {
373+ return types:: EraYear {
374+ // TODO: return era indices?
375+ era_index : None ,
376+ ..CeBce . era_year_from_extended ( year, month, day)
377+ } ;
378+ }
379+ Ok ( index) => data. get ( index) . unwrap ( ) ,
380+ Err ( index) => data. get ( index - 1 ) . unwrap ( ) ,
381+ }
382+ } ;
383+
384+ types:: EraYear {
385+ era,
386+ era_index : None ,
387+ year : year - start. year + 1 ,
388+ extended_year : year,
389+ ambiguity : types:: YearAmbiguity :: CenturyRequired ,
390+ }
391+ }
392+
393+ fn debug_name ( & self ) -> & ' static str {
394+ "Japanese (historical era data)"
395+ }
396+
397+ fn calendar_algorithm ( & self ) -> Option < crate :: preferences:: CalendarAlgorithm > {
398+ None
399+ }
400+ }
401+
294402impl_with_abstract_gregorian ! (
295403 JapaneseExtended ,
296404 JapaneseExtendedDateInner ,
297405 Japanese ,
298406 this,
299- & this. 0
407+ this
300408) ;
301409
302410impl Date < Japanese > {
@@ -411,7 +519,7 @@ impl Date<JapaneseExtended> {
411519 year,
412520 month,
413521 day,
414- & AbstractGregorian ( & japanext_calendar. as_calendar ( ) . 0 ) ,
522+ & AbstractGregorian ( japanext_calendar. as_calendar ( ) ) ,
415523 )
416524 . map ( ArithmeticDate :: cast)
417525 . map ( JapaneseExtendedDateInner )
0 commit comments