icu_calendar/
japanese.rs

1// This file is part of ICU4X. For terms of use, please see the file
2// called LICENSE at the top level of the ICU4X source tree
3// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
4
5//! This module contains types and implementations for the Japanese calendar.
6//!
7//! ```rust
8//! use icu::calendar::japanese::Japanese;
9//! use icu::calendar::{types::Era, Date, DateTime};
10//! use tinystr::tinystr;
11//!
12//! let japanese_calendar = Japanese::new();
13//!
14//! // `Date` type
15//! let date_iso = Date::try_new_iso_date(1970, 1, 2)
16//!     .expect("Failed to initialize ISO Date instance.");
17//! let date_japanese = Date::new_from_iso(date_iso, japanese_calendar.clone());
18//!
19//! // `DateTime` type
20//! let datetime_iso = DateTime::try_new_iso_datetime(1970, 1, 2, 13, 1, 0)
21//!     .expect("Failed to initialize ISO DateTime instance.");
22//! let datetime_japanese =
23//!     DateTime::new_from_iso(datetime_iso, japanese_calendar.clone());
24//!
25//! // `Date` checks
26//! assert_eq!(date_japanese.year().number, 45);
27//! assert_eq!(date_japanese.month().ordinal, 1);
28//! assert_eq!(date_japanese.day_of_month().0, 2);
29//! assert_eq!(date_japanese.year().era, Era(tinystr!(16, "showa")));
30//!
31//! // `DateTime` type
32//! assert_eq!(datetime_japanese.date.year().number, 45);
33//! assert_eq!(datetime_japanese.date.month().ordinal, 1);
34//! assert_eq!(datetime_japanese.date.day_of_month().0, 2);
35//! assert_eq!(
36//!     datetime_japanese.date.year().era,
37//!     Era(tinystr!(16, "showa"))
38//! );
39//! assert_eq!(datetime_japanese.time.hour.number(), 13);
40//! assert_eq!(datetime_japanese.time.minute.number(), 1);
41//! assert_eq!(datetime_japanese.time.second.number(), 0);
42//! ```
43
44use crate::any_calendar::AnyCalendarKind;
45use crate::iso::{Iso, IsoDateInner};
46use crate::provider::{EraStartDate, JapaneseErasV1Marker, JapaneseExtendedErasV1Marker};
47use crate::{
48    types, AsCalendar, Calendar, CalendarError, Date, DateDuration, DateDurationUnit, DateTime,
49    Ref, Time,
50};
51use icu_provider::prelude::*;
52use tinystr::{tinystr, TinyStr16};
53
54/// The [Japanese Calendar] (with modern eras only)
55///
56/// The [Japanese calendar] is a solar calendar used in Japan, with twelve months.
57/// The months and days are identical to that of the Gregorian calendar, however the years are counted
58/// differently using the Japanese era system.
59///
60/// This calendar only contains eras after Meiji, for all historical eras, check out [`JapaneseExtended`].
61///
62/// This type can be used with [`Date`] or [`DateTime`] to represent dates in this calendar.
63///
64/// [Japanese calendar]: https://en.wikipedia.org/wiki/Japanese_calendar
65///
66/// # Era codes
67///
68/// This calendar currently supports seven era codes. It supports the five post-Meiji eras
69/// (`"meiji"`, `"taisho"`, `"showa"`, `"heisei"`, `"reiwa"`), as well as using the Gregorian
70/// `"bce"` and `"ce"` for dates before the Meiji era.
71///
72/// Future eras will also be added to this type when they are decided.
73///
74/// These eras are loaded from data, requiring a data provider capable of providing [`JapaneseErasV1Marker`]
75/// data (`calendar/japanese@1`).
76///
77/// # Month codes
78///
79/// This calendar supports 12 solar month codes (`"M01" - "M12"`)
80#[derive(Clone, Debug, Default)]
81pub struct Japanese {
82    eras: DataPayload<JapaneseErasV1Marker>,
83}
84
85/// The [Japanese Calendar] (with historical eras)
86///
87/// The [Japanese calendar] is a solar calendar used in Japan, with twelve months.
88/// The months and days are identical to that of the Gregorian calendar, however the years are counted
89/// differently using the Japanese era system.
90///
91/// This type can be used with [`Date`] or [`DateTime`] to represent dates in this calendar.
92///
93/// [Japanese calendar]: https://en.wikipedia.org/wiki/Japanese_calendar
94///
95/// # Era codes
96///
97/// This calendar supports a large number of era codes. It supports the five post-Meiji eras
98/// (`"meiji"`, `"taisho"`, `"showa"`, `"heisei"`, `"reiwa"`). Pre-Meiji eras are represented
99/// with their names converted to lowercase ascii and followed by their start year. E.g. the "Ten'ō"
100/// era (781 - 782 CE) has the code `"teno-781"`. The  Gregorian `"bce"` and `"ce"` eras
101/// are used for dates before the first known era era.
102///
103///
104/// These eras are loaded from data, requiring a data provider capable of providing [`JapaneseExtendedErasV1Marker`]
105/// data (`calendar/japanext@1`).
106#[derive(Clone, Debug, Default)]
107pub struct JapaneseExtended(Japanese);
108
109#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
110/// The inner date type used for representing [`Date`]s of [`Japanese`]. See [`Date`] and [`Japanese`] for more details.
111pub struct JapaneseDateInner {
112    inner: IsoDateInner,
113    adjusted_year: i32,
114    era: TinyStr16,
115}
116
117impl Japanese {
118    /// Creates a new [`Japanese`] using only modern eras (post-meiji) from compiled data.
119    ///
120    /// ✨ *Enabled with the `compiled_data` Cargo feature.*
121    ///
122    /// [📚 Help choosing a constructor](icu_provider::constructors)
123    #[cfg(feature = "compiled_data")]
124    pub const fn new() -> Self {
125        Self {
126            eras: DataPayload::from_static_ref(
127                crate::provider::Baked::SINGLETON_CALENDAR_JAPANESE_V1,
128            ),
129        }
130    }
131
132    icu_provider::gen_any_buffer_data_constructors!(locale: skip, options: skip, error: CalendarError,
133        #[cfg(skip)]
134        functions: [
135            new,
136            try_new_with_any_provider,
137            try_new_with_buffer_provider,
138            try_new_unstable,
139            Self,
140    ]);
141
142    #[doc = icu_provider::gen_any_buffer_unstable_docs!(UNSTABLE, Self::new)]
143    pub fn try_new_unstable<D: DataProvider<JapaneseErasV1Marker> + ?Sized>(
144        provider: &D,
145    ) -> Result<Self, CalendarError> {
146        Ok(Self {
147            eras: provider.load(Default::default())?.take_payload()?,
148        })
149    }
150
151    fn japanese_date_from_codes(
152        &self,
153        era: types::Era,
154        year: i32,
155        month_code: types::MonthCode,
156        day: u8,
157        debug_name: &'static str,
158    ) -> Result<JapaneseDateInner, CalendarError> {
159        let month = month_code.parsed();
160        let month = if let Some((month, false)) = month {
161            month
162        } else {
163            return Err(CalendarError::UnknownMonthCode(month_code.0, debug_name));
164        };
165
166        if month > 12 {
167            return Err(CalendarError::UnknownMonthCode(month_code.0, debug_name));
168        }
169
170        self.new_japanese_date_inner(era, year, month, day)
171    }
172
173    pub(crate) const DEBUG_NAME: &'static str = "Japanese";
174}
175
176impl JapaneseExtended {
177    /// Creates a new [`Japanese`] from using all eras (including pre-meiji) from compiled data.
178    ///
179    /// ✨ *Enabled with the `compiled_data` Cargo feature.*
180    ///
181    /// [📚 Help choosing a constructor](icu_provider::constructors)
182    #[cfg(feature = "compiled_data")]
183    pub const fn new() -> Self {
184        Self(Japanese {
185            eras: DataPayload::from_static_ref(
186                crate::provider::Baked::SINGLETON_CALENDAR_JAPANEXT_V1,
187            ),
188        })
189    }
190
191    icu_provider::gen_any_buffer_data_constructors!(locale: skip, options: skip, error: CalendarError,
192        #[cfg(skip)]
193        functions: [
194            new,
195            try_new_with_any_provider,
196            try_new_with_buffer_provider,
197            try_new_unstable,
198            Self,
199    ]);
200
201    #[doc = icu_provider::gen_any_buffer_unstable_docs!(UNSTABLE, Self::new)]
202    pub fn try_new_unstable<D: DataProvider<JapaneseExtendedErasV1Marker> + ?Sized>(
203        provider: &D,
204    ) -> Result<Self, CalendarError> {
205        Ok(Self(Japanese {
206            eras: provider.load(Default::default())?.take_payload()?.cast(),
207        }))
208    }
209
210    pub(crate) const DEBUG_NAME: &'static str = "Japanese (historical era data)";
211}
212
213impl Calendar for Japanese {
214    type DateInner = JapaneseDateInner;
215
216    fn date_from_codes(
217        &self,
218        era: types::Era,
219        year: i32,
220        month_code: types::MonthCode,
221        day: u8,
222    ) -> Result<Self::DateInner, CalendarError> {
223        self.japanese_date_from_codes(era, year, month_code, day, self.debug_name())
224    }
225
226    fn date_from_iso(&self, iso: Date<Iso>) -> JapaneseDateInner {
227        let (adjusted_year, era) = self.adjusted_year_for(iso.inner());
228        JapaneseDateInner {
229            inner: *iso.inner(),
230            adjusted_year,
231            era,
232        }
233    }
234
235    fn date_to_iso(&self, date: &Self::DateInner) -> Date<Iso> {
236        Date::from_raw(date.inner, Iso)
237    }
238
239    fn months_in_year(&self, date: &Self::DateInner) -> u8 {
240        Iso.months_in_year(&date.inner)
241    }
242
243    fn days_in_year(&self, date: &Self::DateInner) -> u16 {
244        Iso.days_in_year(&date.inner)
245    }
246
247    fn days_in_month(&self, date: &Self::DateInner) -> u8 {
248        Iso.days_in_month(&date.inner)
249    }
250
251    fn offset_date(&self, date: &mut Self::DateInner, offset: DateDuration<Self>) {
252        Iso.offset_date(&mut date.inner, offset.cast_unit());
253        let (adjusted_year, era) = self.adjusted_year_for(&date.inner);
254        date.adjusted_year = adjusted_year;
255        date.era = era
256    }
257
258    fn until(
259        &self,
260        date1: &Self::DateInner,
261        date2: &Self::DateInner,
262        _calendar2: &Self,
263        largest_unit: DateDurationUnit,
264        smallest_unit: DateDurationUnit,
265    ) -> DateDuration<Self> {
266        Iso.until(
267            &date1.inner,
268            &date2.inner,
269            &Iso,
270            largest_unit,
271            smallest_unit,
272        )
273        .cast_unit()
274    }
275
276    /// The calendar-specific year represented by `date`
277    fn year(&self, date: &Self::DateInner) -> types::FormattableYear {
278        types::FormattableYear {
279            era: types::Era(date.era),
280            number: date.adjusted_year,
281            cyclic: None,
282            related_iso: None,
283        }
284    }
285
286    fn is_in_leap_year(&self, date: &Self::DateInner) -> bool {
287        Iso.is_in_leap_year(&date.inner)
288    }
289
290    /// The calendar-specific month represented by `date`
291    fn month(&self, date: &Self::DateInner) -> types::FormattableMonth {
292        Iso.month(&date.inner)
293    }
294
295    /// The calendar-specific day-of-month represented by `date`
296    fn day_of_month(&self, date: &Self::DateInner) -> types::DayOfMonth {
297        Iso.day_of_month(&date.inner)
298    }
299
300    /// Information of the day of the year
301    fn day_of_year_info(&self, date: &Self::DateInner) -> types::DayOfYearInfo {
302        let prev_dec_31 = IsoDateInner::dec_31(date.inner.0.year - 1);
303        let next_jan_1 = IsoDateInner::jan_1(date.inner.0.year + 1);
304
305        let prev_dec_31 = self.date_from_iso(Date::from_raw(prev_dec_31, Iso));
306        let next_jan_1 = self.date_from_iso(Date::from_raw(next_jan_1, Iso));
307        types::DayOfYearInfo {
308            day_of_year: Iso::days_in_year_direct(date.inner.0.year),
309            days_in_year: Iso::days_in_year_direct(date.inner.0.year),
310            prev_year: self.year(&prev_dec_31),
311            days_in_prev_year: Iso::days_in_year_direct(prev_dec_31.inner.0.year),
312            next_year: self.year(&next_jan_1),
313        }
314    }
315
316    fn debug_name(&self) -> &'static str {
317        Self::DEBUG_NAME
318    }
319
320    fn any_calendar_kind(&self) -> Option<AnyCalendarKind> {
321        Some(AnyCalendarKind::Japanese)
322    }
323}
324
325impl Calendar for JapaneseExtended {
326    type DateInner = JapaneseDateInner;
327
328    fn date_from_codes(
329        &self,
330        era: types::Era,
331        year: i32,
332        month_code: types::MonthCode,
333        day: u8,
334    ) -> Result<Self::DateInner, CalendarError> {
335        self.0
336            .japanese_date_from_codes(era, year, month_code, day, self.debug_name())
337    }
338
339    fn date_from_iso(&self, iso: Date<Iso>) -> JapaneseDateInner {
340        Japanese::date_from_iso(&self.0, iso)
341    }
342
343    fn date_to_iso(&self, date: &Self::DateInner) -> Date<Iso> {
344        Japanese::date_to_iso(&self.0, date)
345    }
346
347    fn months_in_year(&self, date: &Self::DateInner) -> u8 {
348        Japanese::months_in_year(&self.0, date)
349    }
350
351    fn days_in_year(&self, date: &Self::DateInner) -> u16 {
352        Japanese::days_in_year(&self.0, date)
353    }
354
355    fn days_in_month(&self, date: &Self::DateInner) -> u8 {
356        Japanese::days_in_month(&self.0, date)
357    }
358
359    fn offset_date(&self, date: &mut Self::DateInner, offset: DateDuration<Self>) {
360        Japanese::offset_date(&self.0, date, offset.cast_unit())
361    }
362
363    fn until(
364        &self,
365        date1: &Self::DateInner,
366        date2: &Self::DateInner,
367        calendar2: &Self,
368        largest_unit: DateDurationUnit,
369        smallest_unit: DateDurationUnit,
370    ) -> DateDuration<Self> {
371        Japanese::until(
372            &self.0,
373            date1,
374            date2,
375            &calendar2.0,
376            largest_unit,
377            smallest_unit,
378        )
379        .cast_unit()
380    }
381
382    /// The calendar-specific year represented by `date`
383    fn year(&self, date: &Self::DateInner) -> types::FormattableYear {
384        Japanese::year(&self.0, date)
385    }
386
387    fn is_in_leap_year(&self, date: &Self::DateInner) -> bool {
388        Japanese::is_in_leap_year(&self.0, date)
389    }
390
391    /// The calendar-specific month represented by `date`
392    fn month(&self, date: &Self::DateInner) -> types::FormattableMonth {
393        Japanese::month(&self.0, date)
394    }
395
396    /// The calendar-specific day-of-month represented by `date`
397    fn day_of_month(&self, date: &Self::DateInner) -> types::DayOfMonth {
398        Japanese::day_of_month(&self.0, date)
399    }
400
401    /// Information of the day of the year
402    fn day_of_year_info(&self, date: &Self::DateInner) -> types::DayOfYearInfo {
403        Japanese::day_of_year_info(&self.0, date)
404    }
405
406    fn debug_name(&self) -> &'static str {
407        Self::DEBUG_NAME
408    }
409
410    fn any_calendar_kind(&self) -> Option<AnyCalendarKind> {
411        Some(AnyCalendarKind::JapaneseExtended)
412    }
413}
414
415impl Date<Japanese> {
416    /// Construct a new Japanese Date.
417    ///
418    /// Years are specified in the era provided, and must be in range for Japanese
419    /// eras (e.g. dates past April 30 Heisei 31 must be in Reiwa; "Jun 5 Heisei 31" and "Jan 1 Heisei 32"
420    /// will not be adjusted to being in Reiwa 1 and 2 respectively)
421    ///
422    /// However, dates may always be specified in "bce" or "ce" and they will be adjusted as necessary.
423    ///
424    /// ```rust
425    /// use icu::calendar::japanese::Japanese;
426    /// use icu::calendar::{types, Date, Ref};
427    /// use tinystr::tinystr;
428    ///
429    /// let japanese_calendar = Japanese::new();
430    /// // for easy sharing
431    /// let japanese_calendar = Ref(&japanese_calendar);
432    ///
433    /// let era = types::Era(tinystr!(16, "heisei"));
434    ///
435    /// let date = Date::try_new_japanese_date(era, 14, 1, 2, japanese_calendar)
436    ///     .expect("Constructing a date should succeed");
437    ///
438    /// assert_eq!(date.year().era, era);
439    /// assert_eq!(date.year().number, 14);
440    /// assert_eq!(date.month().ordinal, 1);
441    /// assert_eq!(date.day_of_month().0, 2);
442    ///
443    /// // This function will error for eras that are out of bounds:
444    /// // (Heisei was 32 years long, Heisei 33 is in Reiwa)
445    /// let oob_date =
446    ///     Date::try_new_japanese_date(era, 33, 1, 2, japanese_calendar);
447    /// assert!(oob_date.is_err());
448    ///
449    /// // and for unknown eras
450    /// let fake_era = types::Era(tinystr!(16, "neko")); // 🐱
451    /// let fake_date =
452    ///     Date::try_new_japanese_date(fake_era, 10, 1, 2, japanese_calendar);
453    /// assert!(fake_date.is_err());
454    /// ```
455    pub fn try_new_japanese_date<A: AsCalendar<Calendar = Japanese>>(
456        era: types::Era,
457        year: i32,
458        month: u8,
459        day: u8,
460        japanese_calendar: A,
461    ) -> Result<Date<A>, CalendarError> {
462        let inner = japanese_calendar
463            .as_calendar()
464            .new_japanese_date_inner(era, year, month, day)?;
465        Ok(Date::from_raw(inner, japanese_calendar))
466    }
467}
468
469impl Date<JapaneseExtended> {
470    /// Construct a new Japanese Date with all eras.
471    ///
472    /// Years are specified in the era provided, and must be in range for Japanese
473    /// eras (e.g. dates past April 30 Heisei 31 must be in Reiwa; "Jun 5 Heisei 31" and "Jan 1 Heisei 32"
474    /// will not be adjusted to being in Reiwa 1 and 2 respectively)
475    ///
476    /// However, dates may always be specified in "bce" or "ce" and they will be adjusted as necessary.
477    ///
478    /// ```rust
479    /// use icu::calendar::japanese::JapaneseExtended;
480    /// use icu::calendar::{types, Date, Ref};
481    /// use tinystr::tinystr;
482    ///
483    /// let japanext_calendar = JapaneseExtended::new();
484    /// // for easy sharing
485    /// let japanext_calendar = Ref(&japanext_calendar);
486    ///
487    /// let era = types::Era(tinystr!(16, "kansei-1789"));
488    ///
489    /// let date =
490    ///     Date::try_new_japanese_extended_date(era, 7, 1, 2, japanext_calendar)
491    ///         .expect("Constructing a date should succeed");
492    ///
493    /// assert_eq!(date.year().era, era);
494    /// assert_eq!(date.year().number, 7);
495    /// assert_eq!(date.month().ordinal, 1);
496    /// assert_eq!(date.day_of_month().0, 2);
497    /// ```
498    pub fn try_new_japanese_extended_date<A: AsCalendar<Calendar = JapaneseExtended>>(
499        era: types::Era,
500        year: i32,
501        month: u8,
502        day: u8,
503        japanext_calendar: A,
504    ) -> Result<Date<A>, CalendarError> {
505        let inner = japanext_calendar
506            .as_calendar()
507            .0
508            .new_japanese_date_inner(era, year, month, day)?;
509        Ok(Date::from_raw(inner, japanext_calendar))
510    }
511
512    // For testing era fallback in icu_datetime
513    #[doc(hidden)]
514    pub fn into_japanese_date(self) -> Date<Japanese> {
515        Date::from_raw(self.inner, self.calendar.0)
516    }
517}
518
519impl DateTime<Japanese> {
520    /// Construct a new Japanese datetime from integers.
521    ///
522    /// Years are specified in the era provided.
523    ///
524    /// ```rust
525    /// use icu::calendar::japanese::Japanese;
526    /// use icu::calendar::{types, DateTime};
527    /// use tinystr::tinystr;
528    ///
529    /// let japanese_calendar = Japanese::new();
530    ///
531    /// let era = types::Era(tinystr!(16, "heisei"));
532    ///
533    /// let datetime = DateTime::try_new_japanese_datetime(
534    ///     era,
535    ///     14,
536    ///     1,
537    ///     2,
538    ///     13,
539    ///     1,
540    ///     0,
541    ///     japanese_calendar,
542    /// )
543    /// .expect("Constructing a date should succeed");
544    ///
545    /// assert_eq!(datetime.date.year().era, era);
546    /// assert_eq!(datetime.date.year().number, 14);
547    /// assert_eq!(datetime.date.month().ordinal, 1);
548    /// assert_eq!(datetime.date.day_of_month().0, 2);
549    /// assert_eq!(datetime.time.hour.number(), 13);
550    /// assert_eq!(datetime.time.minute.number(), 1);
551    /// assert_eq!(datetime.time.second.number(), 0);
552    /// ```
553    #[allow(clippy::too_many_arguments)] // it's more convenient to have this many arguments
554                                         // if people wish to construct this by parts they can use
555                                         // Date::try_new_japanese_date() + DateTime::new(date, time)
556    pub fn try_new_japanese_datetime<A: AsCalendar<Calendar = Japanese>>(
557        era: types::Era,
558        year: i32,
559        month: u8,
560        day: u8,
561        hour: u8,
562        minute: u8,
563        second: u8,
564        japanese_calendar: A,
565    ) -> Result<DateTime<A>, CalendarError> {
566        Ok(DateTime {
567            date: Date::try_new_japanese_date(era, year, month, day, japanese_calendar)?,
568            time: Time::try_new(hour, minute, second, 0)?,
569        })
570    }
571}
572
573impl DateTime<JapaneseExtended> {
574    /// Construct a new Japanese datetime from integers with all eras.
575    ///
576    /// Years are specified in the era provided.
577    ///
578    /// ```rust
579    /// use icu::calendar::japanese::JapaneseExtended;
580    /// use icu::calendar::{types, DateTime};
581    /// use tinystr::tinystr;
582    ///
583    /// let japanext_calendar = JapaneseExtended::new();
584    ///
585    /// let era = types::Era(tinystr!(16, "kansei-1789"));
586    ///
587    /// let datetime = DateTime::try_new_japanese_extended_datetime(
588    ///     era,
589    ///     7,
590    ///     1,
591    ///     2,
592    ///     13,
593    ///     1,
594    ///     0,
595    ///     japanext_calendar,
596    /// )
597    /// .expect("Constructing a date should succeed");
598    ///
599    /// assert_eq!(datetime.date.year().era, era);
600    /// assert_eq!(datetime.date.year().number, 7);
601    /// assert_eq!(datetime.date.month().ordinal, 1);
602    /// assert_eq!(datetime.date.day_of_month().0, 2);
603    /// assert_eq!(datetime.time.hour.number(), 13);
604    /// assert_eq!(datetime.time.minute.number(), 1);
605    /// assert_eq!(datetime.time.second.number(), 0);
606    /// ```
607    #[allow(clippy::too_many_arguments)] // it's more convenient to have this many arguments
608                                         // if people wish to construct this by parts they can use
609                                         // Date::try_new_japanese_date() + DateTime::new(date, time)
610    pub fn try_new_japanese_extended_datetime<A: AsCalendar<Calendar = JapaneseExtended>>(
611        era: types::Era,
612        year: i32,
613        month: u8,
614        day: u8,
615        hour: u8,
616        minute: u8,
617        second: u8,
618        japanext_calendar: A,
619    ) -> Result<DateTime<A>, CalendarError> {
620        Ok(DateTime {
621            date: Date::try_new_japanese_extended_date(era, year, month, day, japanext_calendar)?,
622            time: Time::try_new(hour, minute, second, 0)?,
623        })
624    }
625}
626
627const MEIJI_START: EraStartDate = EraStartDate {
628    year: 1868,
629    month: 9,
630    day: 8,
631};
632const TAISHO_START: EraStartDate = EraStartDate {
633    year: 1912,
634    month: 7,
635    day: 30,
636};
637const SHOWA_START: EraStartDate = EraStartDate {
638    year: 1926,
639    month: 12,
640    day: 25,
641};
642const HEISEI_START: EraStartDate = EraStartDate {
643    year: 1989,
644    month: 1,
645    day: 8,
646};
647const REIWA_START: EraStartDate = EraStartDate {
648    year: 2019,
649    month: 5,
650    day: 1,
651};
652
653const FALLBACK_ERA: (EraStartDate, TinyStr16) = (REIWA_START, tinystr!(16, "reiwa"));
654
655impl Japanese {
656    /// Given an ISO date, give year and era for that date in the Japanese calendar
657    ///
658    /// This will also use Gregorian eras for eras that are before the earliest era
659    fn adjusted_year_for(&self, date: &IsoDateInner) -> (i32, TinyStr16) {
660        let date: EraStartDate = date.into();
661        let (start, era) = self.japanese_era_for(date);
662        // The year in which an era starts is Year 1, and it may be short
663        // The only time this function will experience dates that are *before*
664        // the era start date are for the first era (Currently, taika-645
665        // for japanext, meiji for japanese),
666        // In such a case, we instead fall back to Gregorian era codes
667        if date < start {
668            if date.year <= 0 {
669                (1 - date.year, tinystr!(16, "bce"))
670            } else {
671                (date.year, tinystr!(16, "ce"))
672            }
673        } else {
674            (date.year - start.year + 1, era)
675        }
676    }
677
678    /// Given an date, obtain the era data (not counting spliced gregorian eras)
679    fn japanese_era_for(&self, date: EraStartDate) -> (EraStartDate, TinyStr16) {
680        let era_data = self.eras.get();
681        // We optimize for the five "modern" post-Meiji eras, which are stored in a smaller
682        // array and also hardcoded. The hardcoded version is not used if data indicates the
683        // presence of newer eras.
684        if date >= MEIJI_START
685            && era_data.dates_to_eras.last().map(|x| x.1) == Some(tinystr!(16, "reiwa"))
686        {
687            // Fast path in case eras have not changed since this code was written
688            return if date >= REIWA_START {
689                (REIWA_START, tinystr!(16, "reiwa"))
690            } else if date >= HEISEI_START {
691                (HEISEI_START, tinystr!(16, "heisei"))
692            } else if date >= SHOWA_START {
693                (SHOWA_START, tinystr!(16, "showa"))
694            } else if date >= TAISHO_START {
695                (TAISHO_START, tinystr!(16, "taisho"))
696            } else {
697                (MEIJI_START, tinystr!(16, "meiji"))
698            };
699        }
700        let data = &era_data.dates_to_eras;
701        match data.binary_search_by(|(d, _)| d.cmp(&date)) {
702            Ok(index) => data.get(index),
703            Err(index) if index == 0 => data.get(index),
704            Err(index) => data.get(index - 1).or_else(|| data.iter().next_back()),
705        }
706        .unwrap_or(FALLBACK_ERA)
707    }
708
709    /// Returns the range of dates for a given Japanese era code,
710    /// not handling "bce" or "ce"
711    ///
712    /// Returns (era_start, era_end)
713    fn japanese_era_range_for(
714        &self,
715        era: TinyStr16,
716    ) -> Result<(EraStartDate, Option<EraStartDate>), CalendarError> {
717        // Avoid linear search by trying well known eras
718        if era == tinystr!(16, "reiwa") {
719            // Check if we're the last
720            if let Some(last) = self.eras.get().dates_to_eras.last() {
721                if last.1 == era {
722                    return Ok((REIWA_START, None));
723                }
724            }
725        } else if era == tinystr!(16, "heisei") {
726            return Ok((HEISEI_START, Some(REIWA_START)));
727        } else if era == tinystr!(16, "showa") {
728            return Ok((SHOWA_START, Some(HEISEI_START)));
729        } else if era == tinystr!(16, "taisho") {
730            return Ok((TAISHO_START, Some(SHOWA_START)));
731        } else if era == tinystr!(16, "meiji") {
732            return Ok((MEIJI_START, Some(TAISHO_START)));
733        }
734
735        let era_data = self.eras.get();
736        let data = &era_data.dates_to_eras;
737        // Try to avoid linear search by binary searching for the year suffix
738        if let Some(year) = era.split('-').nth(1) {
739            if let Ok(ref int) = year.parse::<i32>() {
740                if let Ok(index) = data.binary_search_by(|(d, _)| d.year.cmp(int)) {
741                    #[allow(clippy::expect_used)] // see expect message
742                    let (era_start, code) = data
743                        .get(index)
744                        .expect("Indexing from successful binary search must succeed");
745                    // There is a slight chance we hit the case where there are two eras in the same year
746                    // There are a couple of rare cases of this, but it's not worth writing a range-based binary search
747                    // to catch them since this is an optimization
748                    if code == era {
749                        return Ok((era_start, data.get(index + 1).map(|e| e.0)));
750                    }
751                }
752            }
753        }
754
755        // Avoidance didn't work. Let's find the era manually, searching back from the present
756        if let Some((index, (start, _))) = data.iter().enumerate().rev().find(|d| d.1 .1 == era) {
757            return Ok((start, data.get(index + 1).map(|e| e.0)));
758        }
759
760        Err(CalendarError::UnknownEra(era, self.debug_name()))
761    }
762
763    fn new_japanese_date_inner(
764        &self,
765        era: types::Era,
766        year: i32,
767        month: u8,
768        day: u8,
769    ) -> Result<JapaneseDateInner, CalendarError> {
770        let cal = Ref(self);
771        if era.0 == tinystr!(16, "bce") {
772            if year <= 0 {
773                return Err(CalendarError::OutOfRange);
774            }
775            return Ok(Date::try_new_iso_date(1 - year, month, day)?
776                .to_calendar(cal)
777                .inner);
778        } else if era.0 == tinystr!(16, "ce") {
779            if year <= 0 {
780                return Err(CalendarError::OutOfRange);
781            }
782            return Ok(Date::try_new_iso_date(year, month, day)?
783                .to_calendar(cal)
784                .inner);
785        }
786
787        let (era_start, next_era_start) = self.japanese_era_range_for(era.0)?;
788
789        let date_in_iso = EraStartDate {
790            year: era_start.year + year - 1,
791            month,
792            day,
793        };
794
795        if date_in_iso < era_start {
796            return Err(CalendarError::OutOfRange);
797        } else if let Some(next_era_start) = next_era_start {
798            if date_in_iso >= next_era_start {
799                return Err(CalendarError::OutOfRange);
800            }
801        }
802
803        let iso = Date::try_new_iso_date(date_in_iso.year, date_in_iso.month, date_in_iso.day)?;
804        Ok(JapaneseDateInner {
805            inner: iso.inner,
806            adjusted_year: year,
807            era: era.0,
808        })
809    }
810}
811
812#[cfg(test)]
813mod tests {
814    use super::*;
815    use crate::Ref;
816
817    fn single_test_roundtrip(calendar: Ref<Japanese>, era: &str, year: i32, month: u8, day: u8) {
818        let era = types::Era(era.parse().expect("era must parse"));
819
820        let date =
821            Date::try_new_japanese_date(era, year, month, day, calendar).unwrap_or_else(|e| {
822                panic!("Failed to construct date with {era:?}, {year}, {month}, {day}: {e}")
823            });
824        let iso = date.to_iso();
825        let reconstructed = Date::new_from_iso(iso, calendar);
826        assert_eq!(
827            date, reconstructed,
828            "Failed to roundtrip with {era:?}, {year}, {month}, {day}"
829        );
830
831        // Extra coverage for https://github.com/unicode-org/icu4x/issues/4968
832        assert_eq!(reconstructed.year().era, era);
833        assert_eq!(reconstructed.year().number, year);
834    }
835
836    fn single_test_roundtrip_ext(
837        calendar: Ref<JapaneseExtended>,
838        era: &str,
839        year: i32,
840        month: u8,
841        day: u8,
842    ) {
843        let era = types::Era(era.parse().expect("era must parse"));
844
845        let date = Date::try_new_japanese_extended_date(era, year, month, day, calendar)
846            .unwrap_or_else(|e| {
847                panic!("Failed to construct date with {era:?}, {year}, {month}, {day}: {e}")
848            });
849        let iso = date.to_iso();
850        let reconstructed = Date::new_from_iso(iso, calendar);
851        assert_eq!(
852            date, reconstructed,
853            "Failed to roundtrip with {era:?}, {year}, {month}, {day}"
854        )
855    }
856
857    // test that the Gregorian eras roundtrip to Japanese ones
858    fn single_test_gregorian_roundtrip_ext(
859        calendar: Ref<JapaneseExtended>,
860        era: &str,
861        year: i32,
862        month: u8,
863        day: u8,
864        era2: &str,
865        year2: i32,
866    ) {
867        let era = types::Era(era.parse().expect("era must parse"));
868        let era2 = types::Era(era2.parse().expect("era must parse"));
869
870        let expected = Date::try_new_japanese_extended_date(era2, year2, month, day, calendar)
871            .unwrap_or_else(|e| {
872                panic!(
873                    "Failed to construct expectation date with {era2:?}, {year2}, {month}, {day}: {e}"
874                )
875            });
876
877        let date = Date::try_new_japanese_extended_date(era, year, month, day, calendar)
878            .unwrap_or_else(|e| {
879                panic!("Failed to construct date with {era:?}, {year}, {month}, {day}: {e}")
880            });
881        let iso = date.to_iso();
882        let reconstructed = Date::new_from_iso(iso, calendar);
883        assert_eq!(
884            expected, reconstructed,
885            "Failed to roundtrip with {era:?}, {year}, {month}, {day} == {era2:?}, {year}"
886        )
887    }
888
889    fn single_test_error(
890        calendar: Ref<Japanese>,
891        era: &str,
892        year: i32,
893        month: u8,
894        day: u8,
895        error: CalendarError,
896    ) {
897        let era = types::Era(era.parse().expect("era must parse"));
898
899        let date = Date::try_new_japanese_date(era, year, month, day, calendar);
900        assert_eq!(
901            date,
902            Err(error),
903            "Construction with {era:?}, {year}, {month}, {day} did not return {error:?}"
904        )
905    }
906
907    fn single_test_error_ext(
908        calendar: Ref<JapaneseExtended>,
909        era: &str,
910        year: i32,
911        month: u8,
912        day: u8,
913        error: CalendarError,
914    ) {
915        let era = types::Era(era.parse().expect("era must parse"));
916
917        let date = Date::try_new_japanese_extended_date(era, year, month, day, calendar);
918        assert_eq!(
919            date,
920            Err(error),
921            "Construction with {era:?}, {year}, {month}, {day} did not return {error:?}"
922        )
923    }
924
925    #[test]
926    fn test_japanese() {
927        let calendar = Japanese::new();
928        let calendar_ext = JapaneseExtended::new();
929        let calendar = Ref(&calendar);
930        let calendar_ext = Ref(&calendar_ext);
931
932        single_test_roundtrip(calendar, "heisei", 12, 3, 1);
933        single_test_roundtrip(calendar, "taisho", 3, 3, 1);
934        // Heisei did not start until later in the year
935        single_test_error(calendar, "heisei", 1, 1, 1, CalendarError::OutOfRange);
936
937        single_test_roundtrip_ext(calendar_ext, "heisei", 12, 3, 1);
938        single_test_roundtrip_ext(calendar_ext, "taisho", 3, 3, 1);
939        single_test_error_ext(calendar_ext, "heisei", 1, 1, 1, CalendarError::OutOfRange);
940
941        single_test_roundtrip_ext(calendar_ext, "hakuho-672", 4, 3, 1);
942        single_test_error(
943            calendar,
944            "hakuho-672",
945            4,
946            3,
947            1,
948            CalendarError::UnknownEra("hakuho-672".parse().unwrap(), "Japanese"),
949        );
950
951        // handle bce/ce
952        single_test_roundtrip(calendar, "bce", 100, 3, 1);
953        single_test_roundtrip(calendar, "bce", 1, 3, 1);
954        single_test_roundtrip(calendar, "ce", 1, 3, 1);
955        single_test_roundtrip(calendar, "ce", 100, 3, 1);
956        single_test_roundtrip_ext(calendar_ext, "ce", 100, 3, 1);
957        single_test_roundtrip(calendar, "ce", 1000, 3, 1);
958        single_test_error(calendar, "ce", 0, 3, 1, CalendarError::OutOfRange);
959        single_test_error(calendar, "bce", -1, 3, 1, CalendarError::OutOfRange);
960
961        // handle the cases where bce/ce get adjusted to different eras
962        // single_test_gregorian_roundtrip(calendar, "ce", 2021, 3, 1, "reiwa", 3);
963        single_test_gregorian_roundtrip_ext(calendar_ext, "ce", 1000, 3, 1, "choho-999", 2);
964        single_test_gregorian_roundtrip_ext(calendar_ext, "ce", 749, 5, 10, "tenpyokampo-749", 1);
965        single_test_gregorian_roundtrip_ext(calendar_ext, "bce", 10, 3, 1, "bce", 10);
966
967        // There were multiple eras in this year
968        // This one is from Apr 14 to July 2
969        single_test_roundtrip_ext(calendar_ext, "tenpyokampo-749", 1, 4, 20);
970        single_test_roundtrip_ext(calendar_ext, "tenpyokampo-749", 1, 4, 14);
971        single_test_roundtrip_ext(calendar_ext, "tenpyokampo-749", 1, 7, 1);
972        single_test_error_ext(
973            calendar_ext,
974            "tenpyokampo-749",
975            1,
976            7,
977            5,
978            CalendarError::OutOfRange,
979        );
980        single_test_error_ext(
981            calendar_ext,
982            "tenpyokampo-749",
983            1,
984            4,
985            13,
986            CalendarError::OutOfRange,
987        );
988    }
989}