icu_calendar/
any_calendar.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//! Module for working with multiple calendars at once
6
7use crate::cal::iso::IsoDateInner;
8use crate::cal::*;
9use crate::error::{DateError, DateFromFieldsError};
10use crate::options::DateFromFieldsOptions;
11use crate::options::{DateAddOptions, DateDifferenceOptions};
12use crate::types::{DateFields, YearInfo};
13use crate::{types, AsCalendar, Calendar, Date, Ref};
14
15use crate::preferences::{CalendarAlgorithm, HijriCalendarAlgorithm};
16use icu_locale_core::preferences::define_preferences;
17use icu_provider::prelude::*;
18
19use core::fmt;
20
21define_preferences!(
22    /// The preferences for calendars formatting.
23    [Copy]
24    CalendarPreferences,
25    {
26        /// The user's preferred calendar system.
27        calendar_algorithm: CalendarAlgorithm
28    }
29);
30
31/// This is a calendar that encompasses all formattable calendars supported by this crate
32///
33/// This allows for the construction of [`Date`] objects that have their calendar known at runtime.
34///
35/// This can be constructed by calling `.into()` on a concrete calendar type if the calendar type is known at
36/// compile time. When the type is known at runtime, the [`AnyCalendar::new()`] and sibling methods may be used.
37///
38/// [`Date`] can also be converted to [`AnyCalendar`]-compatible ones
39/// via [`Date::to_any()`](crate::Date::to_any()).
40///
41/// There are many ways of constructing an AnyCalendar'd date:
42/// ```
43/// use icu::calendar::{AnyCalendar, AnyCalendarKind, Date, cal::{Japanese, Gregorian}, types::MonthCode};
44/// use icu::locale::locale;
45/// use tinystr::tinystr;
46/// # use std::rc::Rc;
47///
48/// let locale = locale!("en-u-ca-japanese"); // English with the Japanese calendar
49///
50/// let calendar = AnyCalendar::new(AnyCalendarKind::new(locale.into()));
51///
52/// // This is a Date<AnyCalendar>
53/// let any_japanese_date = Date::try_new_gregorian(2020, 9, 1)
54///     .expect("Failed to construct Gregorian Date.")
55///     .to_calendar(calendar)
56///     .to_any();
57///
58/// // Construct a date in the appropriate typed calendar and convert
59/// let japanese_calendar = Japanese::new();
60/// let japanese_date = Date::try_new_japanese_with_calendar("reiwa", 2, 9, 1,
61///                                                         japanese_calendar).unwrap();
62/// assert_eq!(japanese_date.to_any(), any_japanese_date);
63///
64/// // this is also Date<AnyCalendar>, but it uses a different calendar
65/// let any_gregorian_date = any_japanese_date.to_calendar(Gregorian).to_any();
66///
67/// // Date<AnyCalendar> does not have a total order
68/// assert!(any_gregorian_date <= any_gregorian_date);
69/// assert!(any_japanese_date <= any_japanese_date);
70/// assert!(!(any_gregorian_date <= any_japanese_date) && !(any_japanese_date <= any_gregorian_date));
71/// ```
72#[derive(Debug, Clone)]
73#[non_exhaustive]
74pub enum AnyCalendar {
75    /// A [`Buddhist`] calendar
76    Buddhist(Buddhist),
77    /// A [`Chinese`] calendar
78    Chinese(ChineseTraditional),
79    /// A [`Coptic`] calendar
80    Coptic(Coptic),
81    /// A [`Dangi`] calendar
82    Dangi(KoreanTraditional),
83    /// An [`Ethiopian`] calendar
84    Ethiopian(Ethiopian),
85    /// A [`Gregorian`] calendar
86    Gregorian(Gregorian),
87    /// A [`Hebrew`] calendar
88    Hebrew(Hebrew),
89    /// An [`Indian`] calendar
90    Indian(Indian),
91    /// A [`HijriTabular`] calendar
92    HijriTabular(Hijri<hijri::TabularAlgorithm>),
93    /// A [`HijriSimulated`] calendar
94    HijriSimulated(Hijri<hijri::AstronomicalSimulation>),
95    /// A [`HijriUmmAlQura`] calendar
96    HijriUmmAlQura(Hijri<hijri::UmmAlQura>),
97    /// An [`Iso`] calendar
98    Iso(Iso),
99    /// A [`Japanese`] calendar
100    Japanese(Japanese),
101    /// A [`JapaneseExtended`] calendar
102    JapaneseExtended(JapaneseExtended),
103    /// A [`Persian`] calendar
104    Persian(Persian),
105    /// A [`Roc`] calendar
106    Roc(Roc),
107}
108
109// TODO(#3469): Decide on the best way to implement Ord.
110/// The inner date type for [`AnyCalendar`]
111#[derive(Clone, PartialEq, Eq, Debug, Copy)]
112#[non_exhaustive]
113pub enum AnyDateInner {
114    /// A date for a [`Buddhist`] calendar
115    Buddhist(<Buddhist as Calendar>::DateInner),
116    /// A date for a [`Chinese`] calendar
117    Chinese(<ChineseTraditional as Calendar>::DateInner),
118    /// A date for a [`Coptic`] calendar
119    Coptic(<Coptic as Calendar>::DateInner),
120    /// A date for a [`Dangi`] calendar
121    Dangi(<KoreanTraditional as Calendar>::DateInner),
122    /// A date for an [`Ethiopian`] calendar
123    Ethiopian(<Ethiopian as Calendar>::DateInner),
124    /// A date for a [`Gregorian`] calendar
125    Gregorian(<Gregorian as Calendar>::DateInner),
126    /// A date for a [`Hebrew`] calendar
127    Hebrew(<Hebrew as Calendar>::DateInner),
128    /// A date for an [`Indian`] calendar
129    Indian(<Indian as Calendar>::DateInner),
130    /// A date for a [`HijriTabular`] calendar
131    HijriTabular(
132        <Hijri<hijri::TabularAlgorithm> as Calendar>::DateInner,
133        hijri::TabularAlgorithm,
134    ),
135    /// A date for a [`HijriSimulated`] calendar
136    HijriSimulated(<Hijri<hijri::AstronomicalSimulation> as Calendar>::DateInner),
137    /// A date for a [`HijriUmmAlQura`] calendar
138    HijriUmmAlQura(<Hijri<hijri::UmmAlQura> as Calendar>::DateInner),
139    /// A date for an [`Iso`] calendar
140    Iso(<Iso as Calendar>::DateInner),
141    /// A date for a [`Japanese`] calendar
142    Japanese(<Japanese as Calendar>::DateInner),
143    /// A date for a [`JapaneseExtended`] calendar
144    JapaneseExtended(<JapaneseExtended as Calendar>::DateInner),
145    /// A date for a [`Persian`] calendar
146    Persian(<Persian as Calendar>::DateInner),
147    /// A date for a [`Roc`] calendar
148    Roc(<Roc as Calendar>::DateInner),
149}
150
151impl PartialOrd for AnyDateInner {
152    #[rustfmt::skip]
153    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
154        use AnyDateInner::*;
155        match (self, other) {
156            (Buddhist(d1), Buddhist(d2)) => d1.partial_cmp(d2),
157            (Chinese(d1), Chinese(d2)) => d1.partial_cmp(d2),
158            (Coptic(d1), Coptic(d2)) => d1.partial_cmp(d2),
159            (Dangi(d1), Dangi(d2)) => d1.partial_cmp(d2),
160            (Ethiopian(d1), Ethiopian(d2)) => d1.partial_cmp(d2),
161            (Gregorian(d1), Gregorian(d2)) => d1.partial_cmp(d2),
162            (Hebrew(d1), Hebrew(d2)) => d1.partial_cmp(d2),
163            (Indian(d1), Indian(d2)) => d1.partial_cmp(d2),
164            (&HijriTabular(ref d1, s1), &HijriTabular(ref d2, s2)) if s1 == s2 => d1.partial_cmp(d2),
165            (HijriSimulated(d1), HijriSimulated(d2)) => d1.partial_cmp(d2),
166            (HijriUmmAlQura(d1), HijriUmmAlQura(d2)) => d1.partial_cmp(d2),
167            (Iso(d1), Iso(d2)) => d1.partial_cmp(d2),
168            (Japanese(d1), Japanese(d2)) => d1.partial_cmp(d2),
169            (JapaneseExtended(d1), JapaneseExtended(d2)) => d1.partial_cmp(d2),
170            (Persian(d1), Persian(d2)) => d1.partial_cmp(d2),
171            (Roc(d1), Roc(d2)) => d1.partial_cmp(d2),
172            _ => None,
173        }
174    }
175}
176
177macro_rules! match_cal_and_date {
178    (match ($cal:ident, $date:ident): ($cal_matched:ident, $date_matched:ident) => $e:expr) => {
179        match ($cal, $date) {
180            (&Self::Buddhist(ref $cal_matched), &AnyDateInner::Buddhist(ref $date_matched)) => $e,
181            (&Self::Chinese(ref $cal_matched), &AnyDateInner::Chinese(ref $date_matched)) => $e,
182            (&Self::Coptic(ref $cal_matched), &AnyDateInner::Coptic(ref $date_matched)) => $e,
183            (&Self::Dangi(ref $cal_matched), &AnyDateInner::Dangi(ref $date_matched)) => $e,
184            (&Self::Ethiopian(ref $cal_matched), &AnyDateInner::Ethiopian(ref $date_matched)) => $e,
185            (&Self::Gregorian(ref $cal_matched), &AnyDateInner::Gregorian(ref $date_matched)) => $e,
186            (&Self::Hebrew(ref $cal_matched), &AnyDateInner::Hebrew(ref $date_matched)) => $e,
187            (&Self::Indian(ref $cal_matched), &AnyDateInner::Indian(ref $date_matched)) => $e,
188            (
189                &Self::HijriTabular(ref $cal_matched),
190                &AnyDateInner::HijriTabular(ref $date_matched, sighting),
191            ) if $cal_matched.0 == sighting => $e,
192            (
193                &Self::HijriSimulated(ref $cal_matched),
194                &AnyDateInner::HijriSimulated(ref $date_matched),
195            ) => $e,
196            (
197                &Self::HijriUmmAlQura(ref $cal_matched),
198                &AnyDateInner::HijriUmmAlQura(ref $date_matched),
199            ) => $e,
200            (&Self::Iso(ref $cal_matched), &AnyDateInner::Iso(ref $date_matched)) => $e,
201            (&Self::Japanese(ref $cal_matched), &AnyDateInner::Japanese(ref $date_matched)) => $e,
202            (
203                &Self::JapaneseExtended(ref $cal_matched),
204                &AnyDateInner::JapaneseExtended(ref $date_matched),
205            ) => $e,
206            (&Self::Persian(ref $cal_matched), &AnyDateInner::Persian(ref $date_matched)) => $e,
207            (&Self::Roc(ref $cal_matched), &AnyDateInner::Roc(ref $date_matched)) => $e,
208            // This is only reached from misuse of from_raw, a semi-internal api
209            _ => panic!("AnyCalendar with mismatched date type"),
210        }
211    };
212}
213
214macro_rules! match_cal {
215    (match $cal:ident: ($cal_matched:ident) => $e:expr) => {
216        match $cal {
217            &Self::Buddhist(ref $cal_matched) => AnyDateInner::Buddhist($e),
218            &Self::Chinese(ref $cal_matched) => AnyDateInner::Chinese($e),
219            &Self::Coptic(ref $cal_matched) => AnyDateInner::Coptic($e),
220            &Self::Dangi(ref $cal_matched) => AnyDateInner::Dangi($e),
221            &Self::Ethiopian(ref $cal_matched) => AnyDateInner::Ethiopian($e),
222            &Self::Gregorian(ref $cal_matched) => AnyDateInner::Gregorian($e),
223            &Self::Hebrew(ref $cal_matched) => AnyDateInner::Hebrew($e),
224            &Self::Indian(ref $cal_matched) => AnyDateInner::Indian($e),
225            &Self::HijriSimulated(ref $cal_matched) => AnyDateInner::HijriSimulated($e),
226            &Self::HijriTabular(ref $cal_matched) => AnyDateInner::HijriTabular($e, $cal_matched.0),
227            &Self::HijriUmmAlQura(ref $cal_matched) => AnyDateInner::HijriUmmAlQura($e),
228            &Self::Iso(ref $cal_matched) => AnyDateInner::Iso($e),
229            &Self::Japanese(ref $cal_matched) => AnyDateInner::Japanese($e),
230            &Self::JapaneseExtended(ref $cal_matched) => AnyDateInner::JapaneseExtended($e),
231            &Self::Persian(ref $cal_matched) => AnyDateInner::Persian($e),
232            &Self::Roc(ref $cal_matched) => AnyDateInner::Roc($e),
233        }
234    };
235}
236
237/// Error returned when comparing two [`Date`]s with [`AnyCalendar`].
238#[derive(Clone, Copy, PartialEq, Debug)]
239#[non_exhaustive]
240#[doc(hidden)] // unstable, not yet graduated
241pub enum AnyCalendarDifferenceError {
242    /// The calendars of the two dates being compared are not equal.
243    ///
244    /// To compare dates in different calendars, convert them to the same calendar first.
245    ///
246    /// # Examples
247    ///
248    /// ```
249    /// use icu::calendar::cal::AnyCalendarDifferenceError;
250    /// use icu::calendar::Date;
251    ///
252    /// let d1 = Date::try_new_gregorian(2000, 1, 1).unwrap().to_any();
253    /// let d2 = Date::try_new_persian(1562, 1, 1).unwrap().to_any();
254    ///
255    /// assert_eq!(
256    ///     d1.try_until_with_options(&d2, Default::default())
257    ///         .unwrap_err(),
258    ///     AnyCalendarDifferenceError::MismatchedCalendars,
259    /// );
260    ///
261    /// // To compare the dates, convert them to the same calendar,
262    /// // such as ISO.
263    ///
264    /// d1.to_iso()
265    ///     .try_until_with_options(&d2.to_iso(), Default::default())
266    ///     .unwrap();
267    /// ```
268    MismatchedCalendars,
269}
270
271impl crate::cal::scaffold::UnstableSealed for AnyCalendar {}
272impl Calendar for AnyCalendar {
273    type DateInner = AnyDateInner;
274    type Year = YearInfo;
275    type DifferenceError = AnyCalendarDifferenceError;
276
277    fn from_codes(
278        &self,
279        era: Option<&str>,
280        year: i32,
281        month_code: types::MonthCode,
282        day: u8,
283    ) -> Result<Self::DateInner, DateError> {
284        Ok(match_cal!(match self: (c) => c.from_codes(era, year, month_code, day)?))
285    }
286
287    #[cfg(feature = "unstable")]
288    fn from_fields(
289        &self,
290        fields: DateFields,
291        options: DateFromFieldsOptions,
292    ) -> Result<Self::DateInner, DateFromFieldsError> {
293        Ok(match_cal!(match self: (c) => c.from_fields(fields, options)?))
294    }
295
296    fn has_cheap_iso_conversion(&self) -> bool {
297        match self {
298            Self::Buddhist(ref c) => c.has_cheap_iso_conversion(),
299            Self::Chinese(ref c) => c.has_cheap_iso_conversion(),
300            Self::Coptic(ref c) => c.has_cheap_iso_conversion(),
301            Self::Dangi(ref c) => c.has_cheap_iso_conversion(),
302            Self::Ethiopian(ref c) => c.has_cheap_iso_conversion(),
303            Self::Gregorian(ref c) => c.has_cheap_iso_conversion(),
304            Self::Hebrew(ref c) => c.has_cheap_iso_conversion(),
305            Self::Indian(ref c) => c.has_cheap_iso_conversion(),
306            Self::HijriSimulated(ref c) => c.has_cheap_iso_conversion(),
307            Self::HijriTabular(ref c) => c.has_cheap_iso_conversion(),
308            Self::HijriUmmAlQura(ref c) => c.has_cheap_iso_conversion(),
309            Self::Iso(ref c) => c.has_cheap_iso_conversion(),
310            Self::Japanese(ref c) => c.has_cheap_iso_conversion(),
311            Self::JapaneseExtended(ref c) => c.has_cheap_iso_conversion(),
312            Self::Persian(ref c) => c.has_cheap_iso_conversion(),
313            Self::Roc(ref c) => c.has_cheap_iso_conversion(),
314        }
315    }
316
317    fn from_iso(&self, iso: IsoDateInner) -> AnyDateInner {
318        match_cal!(match self: (c) => c.from_iso(iso))
319    }
320
321    fn to_iso(&self, date: &Self::DateInner) -> IsoDateInner {
322        match_cal_and_date!(match (self, date): (c, d) => c.to_iso(d))
323    }
324
325    fn from_rata_die(&self, rd: calendrical_calculations::rata_die::RataDie) -> Self::DateInner {
326        match_cal!(match self: (c) => c.from_rata_die(rd))
327    }
328
329    fn to_rata_die(&self, date: &Self::DateInner) -> calendrical_calculations::rata_die::RataDie {
330        match_cal_and_date!(match (self, date): (c, d) => c.to_rata_die(d))
331    }
332
333    fn months_in_year(&self, date: &Self::DateInner) -> u8 {
334        match_cal_and_date!(match (self, date): (c, d) => c.months_in_year(d))
335    }
336
337    fn days_in_year(&self, date: &Self::DateInner) -> u16 {
338        match_cal_and_date!(match (self, date): (c, d) => c.days_in_year(d))
339    }
340
341    fn days_in_month(&self, date: &Self::DateInner) -> u8 {
342        match_cal_and_date!(match (self, date): (c, d) => c.days_in_month(d))
343    }
344
345    #[cfg(feature = "unstable")]
346    fn add(
347        &self,
348        date: &Self::DateInner,
349        duration: types::DateDuration,
350        options: DateAddOptions,
351    ) -> Result<Self::DateInner, DateError> {
352        let mut date = *date;
353        match (self, &mut date) {
354            (Self::Buddhist(c), AnyDateInner::Buddhist(ref mut d)) => {
355                *d = c.add(d, duration, options)?
356            }
357            (Self::Chinese(c), AnyDateInner::Chinese(ref mut d)) => {
358                *d = c.add(d, duration, options)?
359            }
360            (Self::Coptic(c), AnyDateInner::Coptic(ref mut d)) => {
361                *d = c.add(d, duration, options)?
362            }
363            (Self::Dangi(c), AnyDateInner::Dangi(ref mut d)) => *d = c.add(d, duration, options)?,
364            (Self::Ethiopian(c), AnyDateInner::Ethiopian(ref mut d)) => {
365                *d = c.add(d, duration, options)?
366            }
367            (Self::Gregorian(c), AnyDateInner::Gregorian(ref mut d)) => {
368                *d = c.add(d, duration, options)?
369            }
370            (Self::Hebrew(c), AnyDateInner::Hebrew(ref mut d)) => {
371                *d = c.add(d, duration, options)?
372            }
373            (Self::Indian(c), AnyDateInner::Indian(ref mut d)) => {
374                *d = c.add(d, duration, options)?
375            }
376            (Self::HijriTabular(c), AnyDateInner::HijriTabular(ref mut d, sighting))
377                if c.0 == *sighting =>
378            {
379                *d = c.add(d, duration, options)?
380            }
381            (Self::HijriSimulated(c), AnyDateInner::HijriSimulated(ref mut d)) => {
382                *d = c.add(d, duration, options)?
383            }
384            (Self::HijriUmmAlQura(c), AnyDateInner::HijriUmmAlQura(ref mut d)) => {
385                *d = c.add(d, duration, options)?
386            }
387            (Self::Iso(c), AnyDateInner::Iso(ref mut d)) => *d = c.add(d, duration, options)?,
388            (Self::Japanese(c), AnyDateInner::Japanese(ref mut d)) => {
389                *d = c.add(d, duration, options)?
390            }
391            (Self::JapaneseExtended(c), AnyDateInner::JapaneseExtended(ref mut d)) => {
392                *d = c.add(d, duration, options)?
393            }
394            (Self::Persian(c), AnyDateInner::Persian(ref mut d)) => {
395                *d = c.add(d, duration, options)?
396            }
397            (Self::Roc(c), AnyDateInner::Roc(ref mut d)) => *d = c.add(d, duration, options)?,
398            // This is only reached from misuse of from_raw, a semi-internal api
399            #[expect(clippy::panic)]
400            _ => panic!("AnyCalendar with mismatched date type"),
401        }
402        Ok(date)
403    }
404
405    #[cfg(feature = "unstable")]
406    fn until(
407        &self,
408        date1: &Self::DateInner,
409        date2: &Self::DateInner,
410        options: DateDifferenceOptions,
411    ) -> Result<types::DateDuration, Self::DifferenceError> {
412        let Ok(r) = match (self, date1, date2) {
413            (Self::Buddhist(c1), AnyDateInner::Buddhist(d1), AnyDateInner::Buddhist(d2)) => {
414                c1.until(d1, d2, options)
415            }
416            (Self::Chinese(c1), AnyDateInner::Chinese(d1), AnyDateInner::Chinese(d2)) => {
417                c1.until(d1, d2, options)
418            }
419            (Self::Coptic(c1), AnyDateInner::Coptic(d1), AnyDateInner::Coptic(d2)) => {
420                c1.until(d1, d2, options)
421            }
422            (Self::Dangi(c1), AnyDateInner::Dangi(d1), AnyDateInner::Dangi(d2)) => {
423                c1.until(d1, d2, options)
424            }
425            (Self::Ethiopian(c1), AnyDateInner::Ethiopian(d1), AnyDateInner::Ethiopian(d2)) => {
426                c1.until(d1, d2, options)
427            }
428            (Self::Gregorian(c1), AnyDateInner::Gregorian(d1), AnyDateInner::Gregorian(d2)) => {
429                c1.until(d1, d2, options)
430            }
431            (Self::Hebrew(c1), AnyDateInner::Hebrew(d1), AnyDateInner::Hebrew(d2)) => {
432                c1.until(d1, d2, options)
433            }
434            (Self::Indian(c1), AnyDateInner::Indian(d1), AnyDateInner::Indian(d2)) => {
435                c1.until(d1, d2, options)
436            }
437            (
438                Self::HijriTabular(c1),
439                &AnyDateInner::HijriTabular(ref d1, s1),
440                &AnyDateInner::HijriTabular(ref d2, s2),
441            ) if c1.0 == s1 && s1 == s2 => c1.until(d1, d2, options),
442            (
443                Self::HijriSimulated(c1),
444                AnyDateInner::HijriSimulated(d1),
445                AnyDateInner::HijriSimulated(d2),
446            ) => c1.until(d1, d2, options),
447            (
448                Self::HijriUmmAlQura(c1),
449                AnyDateInner::HijriUmmAlQura(d1),
450                AnyDateInner::HijriUmmAlQura(d2),
451            ) => c1.until(d1, d2, options),
452            (Self::Iso(c1), AnyDateInner::Iso(d1), AnyDateInner::Iso(d2)) => {
453                c1.until(d1, d2, options)
454            }
455            (Self::Japanese(c1), AnyDateInner::Japanese(d1), AnyDateInner::Japanese(d2)) => {
456                c1.until(d1, d2, options)
457            }
458            (
459                Self::JapaneseExtended(c1),
460                AnyDateInner::JapaneseExtended(d1),
461                AnyDateInner::JapaneseExtended(d2),
462            ) => c1.until(d1, d2, options),
463            (Self::Persian(c1), AnyDateInner::Persian(d1), AnyDateInner::Persian(d2)) => {
464                c1.until(d1, d2, options)
465            }
466            (Self::Roc(c1), AnyDateInner::Roc(d1), AnyDateInner::Roc(d2)) => {
467                c1.until(d1, d2, options)
468            }
469            _ => {
470                return Err(AnyCalendarDifferenceError::MismatchedCalendars);
471            }
472        };
473        Ok(r)
474    }
475
476    fn year_info(&self, date: &Self::DateInner) -> types::YearInfo {
477        match_cal_and_date!(match (self, date): (c, d) => c.year_info(d).into())
478    }
479
480    /// The calendar-specific check if `date` is in a leap year
481    fn is_in_leap_year(&self, date: &Self::DateInner) -> bool {
482        match_cal_and_date!(match (self, date): (c, d) => c.is_in_leap_year(d))
483    }
484
485    /// The calendar-specific month represented by `date`
486    fn month(&self, date: &Self::DateInner) -> types::MonthInfo {
487        match_cal_and_date!(match (self, date): (c, d) => c.month(d))
488    }
489
490    /// The calendar-specific day-of-month represented by `date`
491    fn day_of_month(&self, date: &Self::DateInner) -> types::DayOfMonth {
492        match_cal_and_date!(match (self, date): (c, d) => c.day_of_month(d))
493    }
494
495    /// Information of the day of the year
496    fn day_of_year(&self, date: &Self::DateInner) -> types::DayOfYear {
497        match_cal_and_date!(match (self, date): (c, d) => c.day_of_year(d))
498    }
499
500    fn debug_name(&self) -> &'static str {
501        match self.kind() {
502            AnyCalendarKind::Buddhist => "AnyCalendar (Buddhist)",
503            AnyCalendarKind::Chinese => "AnyCalendar (Chinese)",
504            AnyCalendarKind::Coptic => "AnyCalendar (Coptic)",
505            AnyCalendarKind::Dangi => "AnyCalendar (Dangi)",
506            AnyCalendarKind::Ethiopian => "AnyCalendar (Ethiopian, Amete Miret)",
507            AnyCalendarKind::EthiopianAmeteAlem => "AnyCalendar (Ethiopian, Amete Alem)",
508            AnyCalendarKind::Gregorian => "AnyCalendar (Gregorian)",
509            AnyCalendarKind::Hebrew => "AnyCalendar (Hebrew)",
510            AnyCalendarKind::Indian => "AnyCalendar (Indian)",
511            AnyCalendarKind::HijriTabularTypeIIFriday => {
512                "AnyCalendar (Hijri, tabular, type II leap years, Friday epoch)"
513            }
514            AnyCalendarKind::HijriTabularTypeIIThursday => {
515                "AnyCalendar (Hijri, tabular, type II leap years, Thursday epoch)"
516            }
517            AnyCalendarKind::HijriSimulatedMecca => "AnyCalendar (Hijri, simulated Mecca)",
518            AnyCalendarKind::HijriUmmAlQura => "AnyCalendar (Hijri, Umm al-Qura)",
519            AnyCalendarKind::Iso => "AnyCalendar (Iso)",
520            AnyCalendarKind::Japanese => "AnyCalendar (Japanese)",
521            AnyCalendarKind::JapaneseExtended => "AnyCalendar (Japanese, historical era data)",
522            AnyCalendarKind::Persian => "AnyCalendar (Persian)",
523            AnyCalendarKind::Roc => "AnyCalendar (Roc)",
524        }
525    }
526
527    fn calendar_algorithm(&self) -> Option<CalendarAlgorithm> {
528        match self {
529            Self::Buddhist(ref c) => c.calendar_algorithm(),
530            Self::Chinese(ref c) => c.calendar_algorithm(),
531            Self::Coptic(ref c) => c.calendar_algorithm(),
532            Self::Dangi(ref c) => c.calendar_algorithm(),
533            Self::Ethiopian(ref c) => c.calendar_algorithm(),
534            Self::Gregorian(ref c) => c.calendar_algorithm(),
535            Self::Hebrew(ref c) => c.calendar_algorithm(),
536            Self::Indian(ref c) => c.calendar_algorithm(),
537            Self::HijriSimulated(ref c) => c.calendar_algorithm(),
538            Self::HijriTabular(ref c) => c.calendar_algorithm(),
539            Self::HijriUmmAlQura(ref c) => c.calendar_algorithm(),
540            Self::Iso(ref c) => c.calendar_algorithm(),
541            Self::Japanese(ref c) => c.calendar_algorithm(),
542            Self::JapaneseExtended(ref c) => c.calendar_algorithm(),
543            Self::Persian(ref c) => c.calendar_algorithm(),
544            Self::Roc(ref c) => c.calendar_algorithm(),
545        }
546    }
547}
548
549impl AnyCalendar {
550    /// Constructs an AnyCalendar for a given calendar kind from compiled data.
551    ///
552    /// ✨ *Enabled with the `compiled_data` Cargo feature.*
553    ///
554    /// [📚 Help choosing a constructor](icu_provider::constructors)
555    #[cfg(feature = "compiled_data")]
556    pub const fn new(kind: AnyCalendarKind) -> Self {
557        match kind {
558            AnyCalendarKind::Buddhist => AnyCalendar::Buddhist(Buddhist),
559            AnyCalendarKind::Chinese => AnyCalendar::Chinese(ChineseTraditional::new()),
560            AnyCalendarKind::Coptic => AnyCalendar::Coptic(Coptic),
561            AnyCalendarKind::Dangi => AnyCalendar::Dangi(KoreanTraditional::new()),
562            AnyCalendarKind::Ethiopian => AnyCalendar::Ethiopian(Ethiopian::new_with_era_style(
563                EthiopianEraStyle::AmeteMihret,
564            )),
565            AnyCalendarKind::EthiopianAmeteAlem => {
566                AnyCalendar::Ethiopian(Ethiopian::new_with_era_style(EthiopianEraStyle::AmeteAlem))
567            }
568            AnyCalendarKind::Gregorian => AnyCalendar::Gregorian(Gregorian),
569            AnyCalendarKind::Hebrew => AnyCalendar::Hebrew(Hebrew),
570            AnyCalendarKind::Indian => AnyCalendar::Indian(Indian),
571            AnyCalendarKind::HijriTabularTypeIIFriday => {
572                AnyCalendar::HijriTabular(Hijri::new_tabular(
573                    hijri::TabularAlgorithmLeapYears::TypeII,
574                    hijri::TabularAlgorithmEpoch::Friday,
575                ))
576            }
577            AnyCalendarKind::HijriSimulatedMecca => {
578                AnyCalendar::HijriSimulated(Hijri::new_simulated_mecca())
579            }
580            AnyCalendarKind::HijriTabularTypeIIThursday => {
581                AnyCalendar::HijriTabular(Hijri::new_tabular(
582                    hijri::TabularAlgorithmLeapYears::TypeII,
583                    hijri::TabularAlgorithmEpoch::Thursday,
584                ))
585            }
586            AnyCalendarKind::HijriUmmAlQura => {
587                AnyCalendar::HijriUmmAlQura(Hijri::new_umm_al_qura())
588            }
589            AnyCalendarKind::Iso => AnyCalendar::Iso(Iso),
590            AnyCalendarKind::Japanese => AnyCalendar::Japanese(Japanese::new()),
591            AnyCalendarKind::JapaneseExtended => {
592                AnyCalendar::JapaneseExtended(JapaneseExtended::new())
593            }
594            AnyCalendarKind::Persian => AnyCalendar::Persian(Persian),
595            AnyCalendarKind::Roc => AnyCalendar::Roc(Roc),
596        }
597    }
598
599    #[cfg(feature = "serde")]
600    #[doc = icu_provider::gen_buffer_unstable_docs!(BUFFER, Self::new)]
601    pub fn try_new_with_buffer_provider<P>(
602        provider: &P,
603        kind: AnyCalendarKind,
604    ) -> Result<Self, DataError>
605    where
606        P: BufferProvider + ?Sized,
607    {
608        Ok(match kind {
609            AnyCalendarKind::Buddhist => AnyCalendar::Buddhist(Buddhist),
610            AnyCalendarKind::Chinese => AnyCalendar::Chinese(ChineseTraditional::new()),
611            AnyCalendarKind::Coptic => AnyCalendar::Coptic(Coptic),
612            AnyCalendarKind::Dangi => AnyCalendar::Dangi(KoreanTraditional::new()),
613            AnyCalendarKind::Ethiopian => AnyCalendar::Ethiopian(Ethiopian::new_with_era_style(
614                EthiopianEraStyle::AmeteMihret,
615            )),
616            AnyCalendarKind::EthiopianAmeteAlem => {
617                AnyCalendar::Ethiopian(Ethiopian::new_with_era_style(EthiopianEraStyle::AmeteAlem))
618            }
619            AnyCalendarKind::Gregorian => AnyCalendar::Gregorian(Gregorian),
620            AnyCalendarKind::Hebrew => AnyCalendar::Hebrew(Hebrew),
621            AnyCalendarKind::Indian => AnyCalendar::Indian(Indian),
622            AnyCalendarKind::HijriTabularTypeIIFriday => {
623                AnyCalendar::HijriTabular(Hijri::new_tabular(
624                    hijri::TabularAlgorithmLeapYears::TypeII,
625                    hijri::TabularAlgorithmEpoch::Friday,
626                ))
627            }
628            AnyCalendarKind::HijriSimulatedMecca => {
629                AnyCalendar::HijriSimulated(Hijri::new_simulated_mecca())
630            }
631            AnyCalendarKind::HijriTabularTypeIIThursday => {
632                AnyCalendar::HijriTabular(Hijri::new_tabular(
633                    hijri::TabularAlgorithmLeapYears::TypeII,
634                    hijri::TabularAlgorithmEpoch::Thursday,
635                ))
636            }
637            AnyCalendarKind::HijriUmmAlQura => {
638                AnyCalendar::HijriUmmAlQura(Hijri::new_umm_al_qura())
639            }
640            AnyCalendarKind::Iso => AnyCalendar::Iso(Iso),
641            AnyCalendarKind::Japanese => {
642                AnyCalendar::Japanese(Japanese::try_new_with_buffer_provider(provider)?)
643            }
644            AnyCalendarKind::JapaneseExtended => AnyCalendar::JapaneseExtended(
645                JapaneseExtended::try_new_with_buffer_provider(provider)?,
646            ),
647            AnyCalendarKind::Persian => AnyCalendar::Persian(Persian),
648            AnyCalendarKind::Roc => AnyCalendar::Roc(Roc),
649        })
650    }
651
652    #[doc = icu_provider::gen_buffer_unstable_docs!(UNSTABLE, Self::new)]
653    pub fn try_new_unstable<P>(provider: &P, kind: AnyCalendarKind) -> Result<Self, DataError>
654    where
655        P: DataProvider<crate::provider::CalendarJapaneseModernV1>
656            + DataProvider<crate::provider::CalendarJapaneseExtendedV1>
657            + ?Sized,
658    {
659        Ok(match kind {
660            AnyCalendarKind::Buddhist => AnyCalendar::Buddhist(Buddhist),
661            AnyCalendarKind::Chinese => AnyCalendar::Chinese(ChineseTraditional::new()),
662            AnyCalendarKind::Coptic => AnyCalendar::Coptic(Coptic),
663            AnyCalendarKind::Dangi => AnyCalendar::Dangi(KoreanTraditional::new()),
664            AnyCalendarKind::Ethiopian => AnyCalendar::Ethiopian(Ethiopian::new_with_era_style(
665                EthiopianEraStyle::AmeteMihret,
666            )),
667            AnyCalendarKind::EthiopianAmeteAlem => {
668                AnyCalendar::Ethiopian(Ethiopian::new_with_era_style(EthiopianEraStyle::AmeteAlem))
669            }
670            AnyCalendarKind::Gregorian => AnyCalendar::Gregorian(Gregorian),
671            AnyCalendarKind::Hebrew => AnyCalendar::Hebrew(Hebrew),
672            AnyCalendarKind::Indian => AnyCalendar::Indian(Indian),
673            AnyCalendarKind::HijriTabularTypeIIFriday => {
674                AnyCalendar::HijriTabular(Hijri::new_tabular(
675                    hijri::TabularAlgorithmLeapYears::TypeII,
676                    hijri::TabularAlgorithmEpoch::Friday,
677                ))
678            }
679            AnyCalendarKind::HijriSimulatedMecca => {
680                AnyCalendar::HijriSimulated(Hijri::new_simulated_mecca())
681            }
682            AnyCalendarKind::HijriTabularTypeIIThursday => {
683                AnyCalendar::HijriTabular(Hijri::new_tabular(
684                    hijri::TabularAlgorithmLeapYears::TypeII,
685                    hijri::TabularAlgorithmEpoch::Thursday,
686                ))
687            }
688            AnyCalendarKind::HijriUmmAlQura => {
689                AnyCalendar::HijriUmmAlQura(Hijri::new_umm_al_qura())
690            }
691            AnyCalendarKind::Iso => AnyCalendar::Iso(Iso),
692            AnyCalendarKind::Japanese => {
693                AnyCalendar::Japanese(Japanese::try_new_unstable(provider)?)
694            }
695            AnyCalendarKind::JapaneseExtended => {
696                AnyCalendar::JapaneseExtended(JapaneseExtended::try_new_unstable(provider)?)
697            }
698            AnyCalendarKind::Persian => AnyCalendar::Persian(Persian),
699            AnyCalendarKind::Roc => AnyCalendar::Roc(Roc),
700        })
701    }
702
703    /// The [`AnyCalendarKind`] corresponding to the calendar this contains
704    pub fn kind(&self) -> AnyCalendarKind {
705        match *self {
706            Self::Buddhist(_) => AnyCalendarKind::Buddhist,
707            Self::Chinese(_) => AnyCalendarKind::Chinese,
708            Self::Coptic(_) => AnyCalendarKind::Coptic,
709            Self::Dangi(_) => AnyCalendarKind::Dangi,
710            Self::Ethiopian(ref e) => IntoAnyCalendar::kind(e),
711            Self::Gregorian(_) => AnyCalendarKind::Gregorian,
712            Self::Hebrew(_) => AnyCalendarKind::Hebrew,
713            Self::Indian(_) => AnyCalendarKind::Indian,
714            Self::HijriTabular(ref h) => IntoAnyCalendar::kind(h),
715            Self::HijriSimulated(ref h) => IntoAnyCalendar::kind(h),
716            Self::HijriUmmAlQura(_) => AnyCalendarKind::HijriUmmAlQura,
717            Self::Iso(_) => AnyCalendarKind::Iso,
718            Self::Japanese(_) => AnyCalendarKind::Japanese,
719            Self::JapaneseExtended(_) => AnyCalendarKind::JapaneseExtended,
720            Self::Persian(_) => AnyCalendarKind::Persian,
721            Self::Roc(_) => AnyCalendarKind::Roc,
722        }
723    }
724}
725
726impl<C: AsCalendar<Calendar = AnyCalendar>> Date<C> {
727    /// Convert this `Date<AnyCalendar>` to another `AnyCalendar`, if conversion is needed
728    pub fn convert_any<'a>(&self, calendar: &'a AnyCalendar) -> Date<Ref<'a, AnyCalendar>> {
729        if calendar.kind() != self.calendar.as_calendar().kind() {
730            Date::new_from_iso(self.to_iso(), Ref(calendar))
731        } else {
732            Date {
733                inner: self.inner,
734                calendar: Ref(calendar),
735            }
736        }
737    }
738}
739
740/// Convenient type for selecting the kind of AnyCalendar to construct
741#[non_exhaustive]
742#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
743pub enum AnyCalendarKind {
744    /// The kind of a [`Buddhist`] calendar
745    ///
746    /// This corresponds to the `"buddhist"` [CLDR calendar](https://unicode.org/reports/tr35/#UnicodeCalendarIdentifier).
747    Buddhist,
748    /// The kind of a [`Chinese`] calendar
749    ///
750    /// This corresponds to the `"chinese"` [CLDR calendar](https://unicode.org/reports/tr35/#UnicodeCalendarIdentifier).
751    Chinese,
752    /// The kind of a [`Coptic`] calendar
753    ///
754    /// This corresponds to the `"coptic"` [CLDR calendar](https://unicode.org/reports/tr35/#UnicodeCalendarIdentifier).
755    Coptic,
756    /// The kind of a [`Dangi`] calendar
757    ///
758    /// This corresponds to the `"dangi"` [CLDR calendar](https://unicode.org/reports/tr35/#UnicodeCalendarIdentifier).
759    Dangi,
760    /// The kind of an [`Ethiopian`] calendar, with Amete Mihret era
761    ///
762    /// This corresponds to the `"ethiopic"` [CLDR calendar](https://unicode.org/reports/tr35/#UnicodeCalendarIdentifier).
763    Ethiopian,
764    /// The kind of an [`Ethiopian`] calendar, with Amete Alem era
765    ///
766    /// This corresponds to the `"ethioaa"` [CLDR calendar](https://unicode.org/reports/tr35/#UnicodeCalendarIdentifier).
767    EthiopianAmeteAlem,
768    /// The kind of a [`Gregorian`] calendar
769    ///
770    /// This corresponds to the `"gregory"` [CLDR calendar](https://unicode.org/reports/tr35/#UnicodeCalendarIdentifier).
771    Gregorian,
772    /// The kind of a [`Hebrew`] calendar
773    ///
774    /// This corresponds to the `"hebrew"` [CLDR calendar](https://unicode.org/reports/tr35/#UnicodeCalendarIdentifier).
775    Hebrew,
776    /// The kind of a [`Indian`] calendar
777    ///
778    /// This corresponds to the `"indian"` [CLDR calendar](https://unicode.org/reports/tr35/#UnicodeCalendarIdentifier).
779    Indian,
780    /// The kind of an [`HijriTabular`] calendar using [`HijriTabularLeapYears::TypeII`] and [`HijriTabularEpoch::Friday`]
781    ///
782    /// This corresponds to the `"islamic-civil"` [CLDR calendar](https://unicode.org/reports/tr35/#UnicodeCalendarIdentifier).
783    HijriTabularTypeIIFriday,
784    /// The kind of an [`HijriSimulated`], Mecca calendar
785    ///
786    /// This corresponds to the `"islamic-rgsa"` [CLDR calendar](https://unicode.org/reports/tr35/#UnicodeCalendarIdentifier).
787    HijriSimulatedMecca,
788    /// The kind of an [`HijriTabular`] calendar using [`HijriTabularLeapYears::TypeII`] and [`HijriTabularEpoch::Thursday`]
789    ///
790    /// This corresponds to the `"islamic-tbla"` [CLDR calendar](https://unicode.org/reports/tr35/#UnicodeCalendarIdentifier).
791    HijriTabularTypeIIThursday,
792    /// The kind of an [`HijriUmmAlQura`] calendar
793    ///
794    /// This corresponds to the `"islamic-umalqura"` [CLDR calendar](https://unicode.org/reports/tr35/#UnicodeCalendarIdentifier).
795    HijriUmmAlQura,
796    /// The kind of an [`Iso`] calendar
797    ///
798    /// This corresponds to the `"iso8601"` [CLDR calendar](https://unicode.org/reports/tr35/#UnicodeCalendarIdentifier).
799    Iso,
800    /// The kind of a [`Japanese`] calendar
801    ///
802    /// This corresponds to the `"japanese"` [CLDR calendar](https://unicode.org/reports/tr35/#UnicodeCalendarIdentifier).
803    Japanese,
804    /// The kind of a [`JapaneseExtended`] calendar
805    ///
806    /// This corresponds to the `"japanext"` [CLDR calendar](https://unicode.org/reports/tr35/#UnicodeCalendarIdentifier).
807    JapaneseExtended,
808    /// The kind of a [`Persian`] calendar
809    ///
810    /// This corresponds to the `"persian"` [CLDR calendar](https://unicode.org/reports/tr35/#UnicodeCalendarIdentifier).
811    Persian,
812    /// The kind of a [`Roc`] calendar
813    ///
814    /// This corresponds to the `"roc"` [CLDR calendar](https://unicode.org/reports/tr35/#UnicodeCalendarIdentifier).
815    Roc,
816}
817
818impl AnyCalendarKind {
819    /// Selects the [`AnyCalendarKind`] appropriate for the given [`CalendarPreferences`].
820    pub fn new(prefs: CalendarPreferences) -> Self {
821        if let Some(kind) = prefs.calendar_algorithm.and_then(|a| a.try_into().ok()) {
822            return kind;
823        }
824
825        // This is tested to be consistent with CLDR in icu_provider_source::calendar::test_calendar_resolution
826        match (
827            prefs.calendar_algorithm,
828            prefs
829                .locale_preferences
830                .region()
831                .as_ref()
832                .map(|r| r.as_str()),
833        ) {
834            (Some(CalendarAlgorithm::Hijri(None)), Some("AE" | "BH" | "KW" | "QA" | "SA")) => {
835                AnyCalendarKind::HijriUmmAlQura
836            }
837            (Some(CalendarAlgorithm::Hijri(None)), _) => AnyCalendarKind::HijriTabularTypeIIFriday,
838            (_, Some("TH")) => AnyCalendarKind::Buddhist,
839            (_, Some("AF" | "IR")) => AnyCalendarKind::Persian,
840            _ => AnyCalendarKind::Gregorian,
841        }
842    }
843}
844
845impl TryFrom<CalendarAlgorithm> for AnyCalendarKind {
846    type Error = ();
847    fn try_from(v: CalendarAlgorithm) -> Result<Self, Self::Error> {
848        use CalendarAlgorithm::*;
849        match v {
850            Buddhist => Ok(AnyCalendarKind::Buddhist),
851            Chinese => Ok(AnyCalendarKind::Chinese),
852            Coptic => Ok(AnyCalendarKind::Coptic),
853            Dangi => Ok(AnyCalendarKind::Dangi),
854            Ethioaa => Ok(AnyCalendarKind::EthiopianAmeteAlem),
855            Ethiopic => Ok(AnyCalendarKind::Ethiopian),
856            Gregory => Ok(AnyCalendarKind::Gregorian),
857            Hebrew => Ok(AnyCalendarKind::Hebrew),
858            Indian => Ok(AnyCalendarKind::Indian),
859            Hijri(None) => Err(()),
860            Hijri(Some(HijriCalendarAlgorithm::Umalqura)) => Ok(AnyCalendarKind::HijriUmmAlQura),
861            Hijri(Some(HijriCalendarAlgorithm::Tbla)) => {
862                Ok(AnyCalendarKind::HijriTabularTypeIIThursday)
863            }
864            Hijri(Some(HijriCalendarAlgorithm::Civil)) => {
865                Ok(AnyCalendarKind::HijriTabularTypeIIFriday)
866            }
867            Hijri(Some(HijriCalendarAlgorithm::Rgsa)) => Ok(AnyCalendarKind::HijriSimulatedMecca),
868            Iso8601 => Ok(AnyCalendarKind::Iso),
869            Japanese => Ok(AnyCalendarKind::Japanese),
870            Persian => Ok(AnyCalendarKind::Persian),
871            Roc => Ok(AnyCalendarKind::Roc),
872            _ => {
873                debug_assert!(false, "unknown calendar algorithm {v:?}");
874                Err(())
875            }
876        }
877    }
878}
879
880impl fmt::Display for AnyCalendarKind {
881    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
882        fmt::Debug::fmt(self, f)
883    }
884}
885
886/// Trait for calendars that may be converted to [`AnyCalendar`]
887pub trait IntoAnyCalendar: Calendar + Sized {
888    /// Convert this calendar into an [`AnyCalendar`], moving it
889    ///
890    /// You should not need to call this method directly
891    fn to_any(self) -> AnyCalendar;
892
893    /// The [`AnyCalendarKind`] enum variant associated with this calendar
894    fn kind(&self) -> AnyCalendarKind;
895
896    /// Move an [`AnyCalendar`] into a `Self`, or returning it as an error
897    /// if the types do not match.
898    ///
899    /// You should not need to call this method directly
900    fn from_any(any: AnyCalendar) -> Result<Self, AnyCalendar>;
901
902    /// Convert an [`AnyCalendar`] reference into a `Self` reference.
903    ///
904    /// You should not need to call this method directly
905    fn from_any_ref(any: &AnyCalendar) -> Option<&Self>;
906
907    /// Convert a date for this calendar into an `AnyDateInner`
908    ///
909    /// You should not need to call this method directly
910    fn date_to_any(&self, d: &Self::DateInner) -> AnyDateInner;
911}
912
913impl IntoAnyCalendar for AnyCalendar {
914    #[inline]
915    fn to_any(self) -> AnyCalendar {
916        self
917    }
918    #[inline]
919    fn kind(&self) -> AnyCalendarKind {
920        self.kind()
921    }
922    #[inline]
923    fn from_any(any: AnyCalendar) -> Result<Self, AnyCalendar> {
924        Ok(any)
925    }
926    #[inline]
927    fn from_any_ref(any: &AnyCalendar) -> Option<&Self> {
928        Some(any)
929    }
930    #[inline]
931    fn date_to_any(&self, d: &Self::DateInner) -> AnyDateInner {
932        *d
933    }
934}
935
936impl IntoAnyCalendar for Buddhist {
937    #[inline]
938    fn to_any(self) -> AnyCalendar {
939        AnyCalendar::Buddhist(Buddhist)
940    }
941    #[inline]
942    fn kind(&self) -> AnyCalendarKind {
943        AnyCalendarKind::Buddhist
944    }
945    #[inline]
946    fn from_any(any: AnyCalendar) -> Result<Self, AnyCalendar> {
947        if let AnyCalendar::Buddhist(cal) = any {
948            Ok(cal)
949        } else {
950            Err(any)
951        }
952    }
953    #[inline]
954    fn from_any_ref(any: &AnyCalendar) -> Option<&Self> {
955        if let AnyCalendar::Buddhist(cal) = any {
956            Some(cal)
957        } else {
958            None
959        }
960    }
961    #[inline]
962    fn date_to_any(&self, d: &Self::DateInner) -> AnyDateInner {
963        AnyDateInner::Buddhist(*d)
964    }
965}
966
967impl From<Buddhist> for AnyCalendar {
968    fn from(value: Buddhist) -> AnyCalendar {
969        value.to_any()
970    }
971}
972
973impl IntoAnyCalendar for ChineseTraditional {
974    #[inline]
975    fn to_any(self) -> AnyCalendar {
976        AnyCalendar::Chinese(self)
977    }
978    #[inline]
979    fn kind(&self) -> AnyCalendarKind {
980        AnyCalendarKind::Chinese
981    }
982    #[inline]
983    fn from_any(any: AnyCalendar) -> Result<Self, AnyCalendar> {
984        if let AnyCalendar::Chinese(cal) = any {
985            Ok(cal)
986        } else {
987            Err(any)
988        }
989    }
990    #[inline]
991    fn from_any_ref(any: &AnyCalendar) -> Option<&Self> {
992        if let AnyCalendar::Chinese(cal) = any {
993            Some(cal)
994        } else {
995            None
996        }
997    }
998    #[inline]
999    fn date_to_any(&self, d: &Self::DateInner) -> AnyDateInner {
1000        AnyDateInner::Chinese(*d)
1001    }
1002}
1003
1004impl From<ChineseTraditional> for AnyCalendar {
1005    fn from(value: ChineseTraditional) -> AnyCalendar {
1006        value.to_any()
1007    }
1008}
1009
1010impl IntoAnyCalendar for Coptic {
1011    #[inline]
1012    fn to_any(self) -> AnyCalendar {
1013        AnyCalendar::Coptic(Coptic)
1014    }
1015    #[inline]
1016    fn kind(&self) -> AnyCalendarKind {
1017        AnyCalendarKind::Coptic
1018    }
1019    #[inline]
1020    fn from_any(any: AnyCalendar) -> Result<Self, AnyCalendar> {
1021        if let AnyCalendar::Coptic(cal) = any {
1022            Ok(cal)
1023        } else {
1024            Err(any)
1025        }
1026    }
1027    #[inline]
1028    fn from_any_ref(any: &AnyCalendar) -> Option<&Self> {
1029        if let AnyCalendar::Coptic(cal) = any {
1030            Some(cal)
1031        } else {
1032            None
1033        }
1034    }
1035    #[inline]
1036    fn date_to_any(&self, d: &Self::DateInner) -> AnyDateInner {
1037        AnyDateInner::Coptic(*d)
1038    }
1039}
1040
1041impl From<Coptic> for AnyCalendar {
1042    fn from(value: Coptic) -> AnyCalendar {
1043        value.to_any()
1044    }
1045}
1046
1047impl IntoAnyCalendar for KoreanTraditional {
1048    #[inline]
1049    fn to_any(self) -> AnyCalendar {
1050        AnyCalendar::Dangi(self)
1051    }
1052    #[inline]
1053    fn kind(&self) -> AnyCalendarKind {
1054        AnyCalendarKind::Dangi
1055    }
1056    #[inline]
1057    fn from_any(any: AnyCalendar) -> Result<Self, AnyCalendar> {
1058        if let AnyCalendar::Dangi(cal) = any {
1059            Ok(cal)
1060        } else {
1061            Err(any)
1062        }
1063    }
1064    #[inline]
1065    fn from_any_ref(any: &AnyCalendar) -> Option<&Self> {
1066        if let AnyCalendar::Dangi(cal) = any {
1067            Some(cal)
1068        } else {
1069            None
1070        }
1071    }
1072    #[inline]
1073    fn date_to_any(&self, d: &Self::DateInner) -> AnyDateInner {
1074        AnyDateInner::Dangi(*d)
1075    }
1076}
1077
1078impl From<KoreanTraditional> for AnyCalendar {
1079    fn from(value: KoreanTraditional) -> AnyCalendar {
1080        value.to_any()
1081    }
1082}
1083
1084impl IntoAnyCalendar for Ethiopian {
1085    // Amete Mihret calendars are the default
1086    #[inline]
1087    fn to_any(self) -> AnyCalendar {
1088        AnyCalendar::Ethiopian(self)
1089    }
1090    #[inline]
1091    fn kind(&self) -> AnyCalendarKind {
1092        match self.era_style() {
1093            EthiopianEraStyle::AmeteAlem => AnyCalendarKind::EthiopianAmeteAlem,
1094            EthiopianEraStyle::AmeteMihret => AnyCalendarKind::Ethiopian,
1095        }
1096    }
1097    #[inline]
1098    fn from_any(any: AnyCalendar) -> Result<Self, AnyCalendar> {
1099        if let AnyCalendar::Ethiopian(cal) = any {
1100            Ok(cal)
1101        } else {
1102            Err(any)
1103        }
1104    }
1105    #[inline]
1106    fn from_any_ref(any: &AnyCalendar) -> Option<&Self> {
1107        if let AnyCalendar::Ethiopian(cal) = any {
1108            Some(cal)
1109        } else {
1110            None
1111        }
1112    }
1113    #[inline]
1114    fn date_to_any(&self, d: &Self::DateInner) -> AnyDateInner {
1115        AnyDateInner::Ethiopian(*d)
1116    }
1117}
1118
1119impl From<Ethiopian> for AnyCalendar {
1120    fn from(value: Ethiopian) -> AnyCalendar {
1121        value.to_any()
1122    }
1123}
1124
1125impl IntoAnyCalendar for Gregorian {
1126    #[inline]
1127    fn to_any(self) -> AnyCalendar {
1128        AnyCalendar::Gregorian(Gregorian)
1129    }
1130    #[inline]
1131    fn kind(&self) -> AnyCalendarKind {
1132        AnyCalendarKind::Gregorian
1133    }
1134    #[inline]
1135    fn from_any(any: AnyCalendar) -> Result<Self, AnyCalendar> {
1136        if let AnyCalendar::Gregorian(cal) = any {
1137            Ok(cal)
1138        } else {
1139            Err(any)
1140        }
1141    }
1142    #[inline]
1143    fn from_any_ref(any: &AnyCalendar) -> Option<&Self> {
1144        if let AnyCalendar::Gregorian(cal) = any {
1145            Some(cal)
1146        } else {
1147            None
1148        }
1149    }
1150    #[inline]
1151    fn date_to_any(&self, d: &Self::DateInner) -> AnyDateInner {
1152        AnyDateInner::Gregorian(*d)
1153    }
1154}
1155
1156impl From<Gregorian> for AnyCalendar {
1157    fn from(value: Gregorian) -> AnyCalendar {
1158        value.to_any()
1159    }
1160}
1161
1162impl IntoAnyCalendar for Hebrew {
1163    #[inline]
1164    fn to_any(self) -> AnyCalendar {
1165        AnyCalendar::Hebrew(Hebrew)
1166    }
1167    #[inline]
1168    fn kind(&self) -> AnyCalendarKind {
1169        AnyCalendarKind::Hebrew
1170    }
1171    #[inline]
1172    fn from_any(any: AnyCalendar) -> Result<Self, AnyCalendar> {
1173        if let AnyCalendar::Hebrew(cal) = any {
1174            Ok(cal)
1175        } else {
1176            Err(any)
1177        }
1178    }
1179    #[inline]
1180    fn from_any_ref(any: &AnyCalendar) -> Option<&Self> {
1181        if let AnyCalendar::Hebrew(cal) = any {
1182            Some(cal)
1183        } else {
1184            None
1185        }
1186    }
1187    #[inline]
1188    fn date_to_any(&self, d: &Self::DateInner) -> AnyDateInner {
1189        AnyDateInner::Hebrew(*d)
1190    }
1191}
1192
1193impl From<Hebrew> for AnyCalendar {
1194    fn from(value: Hebrew) -> AnyCalendar {
1195        value.to_any()
1196    }
1197}
1198
1199impl IntoAnyCalendar for Indian {
1200    #[inline]
1201    fn to_any(self) -> AnyCalendar {
1202        AnyCalendar::Indian(Indian)
1203    }
1204    #[inline]
1205    fn kind(&self) -> AnyCalendarKind {
1206        AnyCalendarKind::Indian
1207    }
1208    #[inline]
1209    fn from_any(any: AnyCalendar) -> Result<Self, AnyCalendar> {
1210        if let AnyCalendar::Indian(cal) = any {
1211            Ok(cal)
1212        } else {
1213            Err(any)
1214        }
1215    }
1216    #[inline]
1217    fn from_any_ref(any: &AnyCalendar) -> Option<&Self> {
1218        if let AnyCalendar::Indian(cal) = any {
1219            Some(cal)
1220        } else {
1221            None
1222        }
1223    }
1224    #[inline]
1225    fn date_to_any(&self, d: &Self::DateInner) -> AnyDateInner {
1226        AnyDateInner::Indian(*d)
1227    }
1228}
1229
1230impl From<Indian> for AnyCalendar {
1231    fn from(value: Indian) -> AnyCalendar {
1232        value.to_any()
1233    }
1234}
1235
1236impl IntoAnyCalendar for Hijri<hijri::TabularAlgorithm> {
1237    #[inline]
1238    fn to_any(self) -> AnyCalendar {
1239        AnyCalendar::HijriTabular(self)
1240    }
1241    #[inline]
1242    fn kind(&self) -> AnyCalendarKind {
1243        match self.0 {
1244            hijri::TabularAlgorithm {
1245                leap_years: hijri::TabularAlgorithmLeapYears::TypeII,
1246                epoch: hijri::TabularAlgorithmEpoch::Friday,
1247            } => AnyCalendarKind::HijriTabularTypeIIFriday,
1248            hijri::TabularAlgorithm {
1249                leap_years: hijri::TabularAlgorithmLeapYears::TypeII,
1250                epoch: hijri::TabularAlgorithmEpoch::Thursday,
1251            } => AnyCalendarKind::HijriTabularTypeIIThursday,
1252        }
1253    }
1254    #[inline]
1255    fn from_any(any: AnyCalendar) -> Result<Self, AnyCalendar> {
1256        if let AnyCalendar::HijriTabular(cal) = any {
1257            Ok(cal)
1258        } else {
1259            Err(any)
1260        }
1261    }
1262    #[inline]
1263    fn from_any_ref(any: &AnyCalendar) -> Option<&Self> {
1264        if let AnyCalendar::HijriTabular(cal) = any {
1265            Some(cal)
1266        } else {
1267            None
1268        }
1269    }
1270    #[inline]
1271    fn date_to_any(&self, d: &Self::DateInner) -> AnyDateInner {
1272        AnyDateInner::HijriTabular(*d, self.0)
1273    }
1274}
1275
1276impl From<Hijri<hijri::TabularAlgorithm>> for AnyCalendar {
1277    fn from(value: Hijri<hijri::TabularAlgorithm>) -> AnyCalendar {
1278        value.to_any()
1279    }
1280}
1281
1282impl IntoAnyCalendar for Hijri<hijri::AstronomicalSimulation> {
1283    #[inline]
1284    fn to_any(self) -> AnyCalendar {
1285        AnyCalendar::HijriSimulated(self)
1286    }
1287    #[inline]
1288    fn kind(&self) -> AnyCalendarKind {
1289        match self.0.location {
1290            crate::cal::hijri_internal::SimulatedLocation::Mecca => {
1291                AnyCalendarKind::HijriSimulatedMecca
1292            }
1293        }
1294    }
1295    #[inline]
1296    fn from_any(any: AnyCalendar) -> Result<Self, AnyCalendar> {
1297        if let AnyCalendar::HijriSimulated(cal) = any {
1298            Ok(cal)
1299        } else {
1300            Err(any)
1301        }
1302    }
1303    #[inline]
1304    fn from_any_ref(any: &AnyCalendar) -> Option<&Self> {
1305        if let AnyCalendar::HijriSimulated(cal) = any {
1306            Some(cal)
1307        } else {
1308            None
1309        }
1310    }
1311    #[inline]
1312    fn date_to_any(&self, d: &Self::DateInner) -> AnyDateInner {
1313        AnyDateInner::HijriSimulated(*d)
1314    }
1315}
1316
1317impl From<Hijri<hijri::AstronomicalSimulation>> for AnyCalendar {
1318    fn from(value: Hijri<hijri::AstronomicalSimulation>) -> AnyCalendar {
1319        value.to_any()
1320    }
1321}
1322
1323impl IntoAnyCalendar for Hijri<hijri::UmmAlQura> {
1324    #[inline]
1325    fn to_any(self) -> AnyCalendar {
1326        AnyCalendar::HijriUmmAlQura(self)
1327    }
1328    #[inline]
1329    fn kind(&self) -> AnyCalendarKind {
1330        AnyCalendarKind::HijriUmmAlQura
1331    }
1332    #[inline]
1333    fn from_any(any: AnyCalendar) -> Result<Self, AnyCalendar> {
1334        if let AnyCalendar::HijriUmmAlQura(cal) = any {
1335            Ok(cal)
1336        } else {
1337            Err(any)
1338        }
1339    }
1340    #[inline]
1341    fn from_any_ref(any: &AnyCalendar) -> Option<&Self> {
1342        if let AnyCalendar::HijriUmmAlQura(cal) = any {
1343            Some(cal)
1344        } else {
1345            None
1346        }
1347    }
1348    #[inline]
1349    fn date_to_any(&self, d: &Self::DateInner) -> AnyDateInner {
1350        AnyDateInner::HijriUmmAlQura(*d)
1351    }
1352}
1353
1354impl From<Hijri<hijri::UmmAlQura>> for AnyCalendar {
1355    fn from(value: Hijri<hijri::UmmAlQura>) -> AnyCalendar {
1356        value.to_any()
1357    }
1358}
1359
1360impl IntoAnyCalendar for Iso {
1361    #[inline]
1362    fn to_any(self) -> AnyCalendar {
1363        AnyCalendar::Iso(Iso)
1364    }
1365    #[inline]
1366    fn kind(&self) -> AnyCalendarKind {
1367        AnyCalendarKind::Iso
1368    }
1369    #[inline]
1370    fn from_any(any: AnyCalendar) -> Result<Self, AnyCalendar> {
1371        if let AnyCalendar::Iso(cal) = any {
1372            Ok(cal)
1373        } else {
1374            Err(any)
1375        }
1376    }
1377    #[inline]
1378    fn from_any_ref(any: &AnyCalendar) -> Option<&Self> {
1379        if let AnyCalendar::Iso(cal) = any {
1380            Some(cal)
1381        } else {
1382            None
1383        }
1384    }
1385    #[inline]
1386    fn date_to_any(&self, d: &Self::DateInner) -> AnyDateInner {
1387        AnyDateInner::Iso(*d)
1388    }
1389}
1390
1391impl From<Iso> for AnyCalendar {
1392    fn from(value: Iso) -> AnyCalendar {
1393        value.to_any()
1394    }
1395}
1396
1397impl IntoAnyCalendar for Japanese {
1398    #[inline]
1399    fn to_any(self) -> AnyCalendar {
1400        AnyCalendar::Japanese(self)
1401    }
1402    #[inline]
1403    fn kind(&self) -> AnyCalendarKind {
1404        AnyCalendarKind::Japanese
1405    }
1406    #[inline]
1407    fn from_any(any: AnyCalendar) -> Result<Self, AnyCalendar> {
1408        if let AnyCalendar::Japanese(cal) = any {
1409            Ok(cal)
1410        } else {
1411            Err(any)
1412        }
1413    }
1414    #[inline]
1415    fn from_any_ref(any: &AnyCalendar) -> Option<&Self> {
1416        if let AnyCalendar::Japanese(cal) = any {
1417            Some(cal)
1418        } else {
1419            None
1420        }
1421    }
1422    #[inline]
1423    fn date_to_any(&self, d: &Self::DateInner) -> AnyDateInner {
1424        AnyDateInner::Japanese(*d)
1425    }
1426}
1427
1428impl From<Japanese> for AnyCalendar {
1429    fn from(value: Japanese) -> AnyCalendar {
1430        value.to_any()
1431    }
1432}
1433
1434impl IntoAnyCalendar for JapaneseExtended {
1435    #[inline]
1436    fn to_any(self) -> AnyCalendar {
1437        AnyCalendar::JapaneseExtended(self)
1438    }
1439    #[inline]
1440    fn kind(&self) -> AnyCalendarKind {
1441        AnyCalendarKind::JapaneseExtended
1442    }
1443    #[inline]
1444    fn from_any(any: AnyCalendar) -> Result<Self, AnyCalendar> {
1445        if let AnyCalendar::JapaneseExtended(cal) = any {
1446            Ok(cal)
1447        } else {
1448            Err(any)
1449        }
1450    }
1451    #[inline]
1452    fn from_any_ref(any: &AnyCalendar) -> Option<&Self> {
1453        if let AnyCalendar::JapaneseExtended(cal) = any {
1454            Some(cal)
1455        } else {
1456            None
1457        }
1458    }
1459    #[inline]
1460    fn date_to_any(&self, d: &Self::DateInner) -> AnyDateInner {
1461        AnyDateInner::JapaneseExtended(*d)
1462    }
1463}
1464
1465impl From<JapaneseExtended> for AnyCalendar {
1466    fn from(value: JapaneseExtended) -> AnyCalendar {
1467        value.to_any()
1468    }
1469}
1470
1471impl IntoAnyCalendar for Persian {
1472    #[inline]
1473    fn to_any(self) -> AnyCalendar {
1474        AnyCalendar::Persian(Persian)
1475    }
1476    #[inline]
1477    fn kind(&self) -> AnyCalendarKind {
1478        AnyCalendarKind::Persian
1479    }
1480    #[inline]
1481    fn from_any(any: AnyCalendar) -> Result<Self, AnyCalendar> {
1482        if let AnyCalendar::Persian(cal) = any {
1483            Ok(cal)
1484        } else {
1485            Err(any)
1486        }
1487    }
1488    #[inline]
1489    fn from_any_ref(any: &AnyCalendar) -> Option<&Self> {
1490        if let AnyCalendar::Persian(cal) = any {
1491            Some(cal)
1492        } else {
1493            None
1494        }
1495    }
1496    #[inline]
1497    fn date_to_any(&self, d: &Self::DateInner) -> AnyDateInner {
1498        AnyDateInner::Persian(*d)
1499    }
1500}
1501
1502impl From<Persian> for AnyCalendar {
1503    fn from(value: Persian) -> AnyCalendar {
1504        value.to_any()
1505    }
1506}
1507
1508impl IntoAnyCalendar for Roc {
1509    #[inline]
1510    fn to_any(self) -> AnyCalendar {
1511        AnyCalendar::Roc(Roc)
1512    }
1513    #[inline]
1514    fn kind(&self) -> AnyCalendarKind {
1515        AnyCalendarKind::Roc
1516    }
1517    #[inline]
1518    fn from_any(any: AnyCalendar) -> Result<Self, AnyCalendar> {
1519        if let AnyCalendar::Roc(cal) = any {
1520            Ok(cal)
1521        } else {
1522            Err(any)
1523        }
1524    }
1525    #[inline]
1526    fn from_any_ref(any: &AnyCalendar) -> Option<&Self> {
1527        if let AnyCalendar::Roc(cal) = any {
1528            Some(cal)
1529        } else {
1530            None
1531        }
1532    }
1533    #[inline]
1534    fn date_to_any(&self, d: &Self::DateInner) -> AnyDateInner {
1535        AnyDateInner::Roc(*d)
1536    }
1537}
1538
1539impl From<Roc> for AnyCalendar {
1540    fn from(value: Roc) -> AnyCalendar {
1541        value.to_any()
1542    }
1543}
1544
1545#[cfg(test)]
1546mod tests {
1547    use tinystr::tinystr;
1548    use types::MonthCode;
1549
1550    use super::*;
1551    use crate::Ref;
1552
1553    #[track_caller]
1554    fn single_test_roundtrip(
1555        calendar: Ref<AnyCalendar>,
1556        era: Option<(&str, Option<u8>)>,
1557        year: i32,
1558        month_code: &str,
1559        day: u8,
1560    ) {
1561        let month = types::MonthCode(month_code.parse().expect("month code must parse"));
1562
1563        let date = Date::try_new_from_codes(era.map(|x| x.0), year, month, day, calendar)
1564            .unwrap_or_else(|e| {
1565                panic!(
1566                    "Failed to construct date for {} with {era:?}, {year}, {month}, {day}: {e:?}",
1567                    calendar.debug_name(),
1568                )
1569            });
1570
1571        let roundtrip_year = date.year();
1572        let roundtrip_month = date.month().standard_code;
1573        let roundtrip_day = date.day_of_month().0;
1574
1575        assert_eq!(
1576            (month, day),
1577            (roundtrip_month, roundtrip_day),
1578            "Failed to roundtrip for calendar {}",
1579            calendar.debug_name()
1580        );
1581
1582        if let Some((era_code, era_index)) = era {
1583            let roundtrip_era_year = date.year().era().expect("year type should be era");
1584
1585            let roundtrip_year = roundtrip_year.era_year_or_related_iso();
1586            assert_eq!(
1587                (era_code, era_index, year),
1588                (
1589                    roundtrip_era_year.era.as_str(),
1590                    roundtrip_era_year.era_index,
1591                    roundtrip_year
1592                ),
1593                "Failed to roundtrip era for calendar {}",
1594                calendar.debug_name()
1595            )
1596        } else {
1597            assert_eq!(
1598                year,
1599                date.extended_year(),
1600                "Failed to roundtrip year for calendar {}",
1601                calendar.debug_name()
1602            );
1603        }
1604
1605        let iso = date.to_iso();
1606        let reconstructed = Date::new_from_iso(iso, calendar);
1607        assert_eq!(
1608            date, reconstructed,
1609            "Failed to roundtrip via iso with {era:?}, {year}, {month}, {day}"
1610        )
1611    }
1612
1613    #[track_caller]
1614    fn single_test_error(
1615        calendar: Ref<AnyCalendar>,
1616        era: Option<(&str, Option<u8>)>,
1617        year: i32,
1618        month_code: &str,
1619        day: u8,
1620        error: DateError,
1621    ) {
1622        let month = types::MonthCode(month_code.parse().expect("month code must parse"));
1623
1624        let date = Date::try_new_from_codes(era.map(|x| x.0), year, month, day, calendar);
1625        assert_eq!(
1626            date,
1627            Err(error),
1628            "Construction with {era:?}, {year}, {month}, {day} did not return {error:?}"
1629        )
1630    }
1631
1632    #[test]
1633    fn test_any_construction() {
1634        let buddhist = AnyCalendar::new(AnyCalendarKind::Buddhist);
1635        let chinese = AnyCalendar::new(AnyCalendarKind::Chinese);
1636        let coptic = AnyCalendar::new(AnyCalendarKind::Coptic);
1637        let dangi = AnyCalendar::new(AnyCalendarKind::Dangi);
1638        let ethioaa = AnyCalendar::new(AnyCalendarKind::EthiopianAmeteAlem);
1639        let ethiopian = AnyCalendar::new(AnyCalendarKind::Ethiopian);
1640        let gregorian = AnyCalendar::new(AnyCalendarKind::Gregorian);
1641        let hebrew = AnyCalendar::new(AnyCalendarKind::Hebrew);
1642        let indian = AnyCalendar::new(AnyCalendarKind::Indian);
1643        let hijri_civil: AnyCalendar = AnyCalendar::new(AnyCalendarKind::HijriTabularTypeIIFriday);
1644        let hijri_simulated: AnyCalendar = AnyCalendar::new(AnyCalendarKind::HijriSimulatedMecca);
1645        let hijri_astronomical: AnyCalendar =
1646            AnyCalendar::new(AnyCalendarKind::HijriTabularTypeIIThursday);
1647        let hijri_umm_al_qura: AnyCalendar = AnyCalendar::new(AnyCalendarKind::HijriUmmAlQura);
1648        let japanese = AnyCalendar::new(AnyCalendarKind::Japanese);
1649        let japanext = AnyCalendar::new(AnyCalendarKind::JapaneseExtended);
1650        let persian = AnyCalendar::new(AnyCalendarKind::Persian);
1651        let roc = AnyCalendar::new(AnyCalendarKind::Roc);
1652        let buddhist = Ref(&buddhist);
1653        let chinese = Ref(&chinese);
1654        let coptic = Ref(&coptic);
1655        let dangi = Ref(&dangi);
1656        let ethioaa = Ref(&ethioaa);
1657        let ethiopian = Ref(&ethiopian);
1658        let gregorian = Ref(&gregorian);
1659        let hebrew = Ref(&hebrew);
1660        let indian = Ref(&indian);
1661        let hijri_civil = Ref(&hijri_civil);
1662        let hijri_simulated = Ref(&hijri_simulated);
1663        let hijri_astronomical = Ref(&hijri_astronomical);
1664        let hijri_umm_al_qura = Ref(&hijri_umm_al_qura);
1665        let japanese = Ref(&japanese);
1666        let japanext = Ref(&japanext);
1667        let persian = Ref(&persian);
1668        let roc = Ref(&roc);
1669
1670        single_test_roundtrip(buddhist, Some(("be", Some(0))), 100, "M03", 1);
1671        single_test_roundtrip(buddhist, None, 100, "M03", 1);
1672        single_test_roundtrip(buddhist, None, -100, "M03", 1);
1673        single_test_roundtrip(buddhist, Some(("be", Some(0))), -100, "M03", 1);
1674        single_test_error(
1675            buddhist,
1676            Some(("be", Some(0))),
1677            100,
1678            "M13",
1679            1,
1680            DateError::UnknownMonthCode(MonthCode(tinystr!(4, "M13"))),
1681        );
1682
1683        single_test_roundtrip(coptic, Some(("am", Some(0))), 100, "M03", 1);
1684        single_test_roundtrip(coptic, None, 2000, "M03", 1);
1685        single_test_roundtrip(coptic, None, -100, "M03", 1);
1686        single_test_roundtrip(coptic, Some(("am", Some(0))), -99, "M03", 1);
1687        single_test_roundtrip(coptic, Some(("am", Some(0))), 100, "M13", 1);
1688        single_test_error(
1689            coptic,
1690            Some(("am", Some(0))),
1691            100,
1692            "M14",
1693            1,
1694            DateError::UnknownMonthCode(MonthCode(tinystr!(4, "M14"))),
1695        );
1696
1697        single_test_roundtrip(ethiopian, Some(("am", Some(1))), 100, "M03", 1);
1698        single_test_roundtrip(ethiopian, None, 2000, "M03", 1);
1699        single_test_roundtrip(ethiopian, None, -100, "M03", 1);
1700        single_test_roundtrip(ethiopian, Some(("am", Some(1))), 2000, "M13", 1);
1701        single_test_roundtrip(ethiopian, Some(("aa", Some(0))), 5400, "M03", 1);
1702        // Since #6910, the era range is not enforced in try_from_codes
1703        /*
1704        single_test_error(
1705            ethiopian,
1706            Some(("am", Some(0))),
1707            0,
1708            "M03",
1709            1,
1710            DateError::Range {
1711                field: "year",
1712                value: 0,
1713                min: 1,
1714                max: i32::MAX,
1715            },
1716        );
1717        single_test_error(
1718            ethiopian,
1719            Some(("aa", Some(0))),
1720            5600,
1721            "M03",
1722            1,
1723            DateError::Range {
1724                field: "year",
1725                value: 5600,
1726                min: i32::MIN,
1727                max: 5500,
1728            },
1729        );
1730        */
1731        single_test_error(
1732            ethiopian,
1733            Some(("am", Some(0))),
1734            100,
1735            "M14",
1736            1,
1737            DateError::UnknownMonthCode(MonthCode(tinystr!(4, "M14"))),
1738        );
1739
1740        single_test_roundtrip(ethioaa, Some(("aa", Some(0))), 7000, "M13", 1);
1741        single_test_roundtrip(ethioaa, None, 7000, "M13", 1);
1742        single_test_roundtrip(ethioaa, None, -100, "M13", 1);
1743        single_test_roundtrip(ethioaa, Some(("aa", Some(0))), 100, "M03", 1);
1744        single_test_error(
1745            ethiopian,
1746            Some(("aa", Some(0))),
1747            100,
1748            "M14",
1749            1,
1750            DateError::UnknownMonthCode(MonthCode(tinystr!(4, "M14"))),
1751        );
1752
1753        single_test_roundtrip(gregorian, Some(("ce", Some(1))), 100, "M03", 1);
1754        single_test_roundtrip(gregorian, None, 2000, "M03", 1);
1755        single_test_roundtrip(gregorian, None, -100, "M03", 1);
1756        single_test_roundtrip(gregorian, Some(("bce", Some(0))), 100, "M03", 1);
1757        // Since #6910, the era range is not enforced in try_from_codes
1758        /*
1759        single_test_error(
1760            gregorian,
1761            Some(("ce", Some(1))),
1762            0,
1763            "M03",
1764            1,
1765            DateError::Range {
1766                field: "year",
1767                value: 0,
1768                min: 1,
1769                max: i32::MAX,
1770            },
1771        );
1772        single_test_error(
1773            gregorian,
1774            Some(("bce", Some(0))),
1775            0,
1776            "M03",
1777            1,
1778            DateError::Range {
1779                field: "year",
1780                value: 0,
1781                min: 1,
1782                max: i32::MAX,
1783            },
1784        );
1785        */
1786        single_test_error(
1787            gregorian,
1788            Some(("bce", Some(0))),
1789            100,
1790            "M13",
1791            1,
1792            DateError::UnknownMonthCode(MonthCode(tinystr!(4, "M13"))),
1793        );
1794
1795        single_test_roundtrip(indian, Some(("shaka", Some(0))), 100, "M03", 1);
1796        single_test_roundtrip(indian, None, 2000, "M12", 1);
1797        single_test_roundtrip(indian, None, -100, "M03", 1);
1798        single_test_roundtrip(indian, Some(("shaka", Some(0))), 0, "M03", 1);
1799        single_test_error(
1800            indian,
1801            Some(("shaka", Some(0))),
1802            100,
1803            "M13",
1804            1,
1805            DateError::UnknownMonthCode(MonthCode(tinystr!(4, "M13"))),
1806        );
1807
1808        single_test_roundtrip(chinese, None, 400, "M02", 5);
1809        single_test_roundtrip(chinese, None, 4660, "M07", 29);
1810        single_test_roundtrip(chinese, None, -100, "M11", 12);
1811        single_test_error(
1812            chinese,
1813            None,
1814            4658,
1815            "M13",
1816            1,
1817            DateError::UnknownMonthCode(MonthCode(tinystr!(4, "M13"))),
1818        );
1819
1820        single_test_roundtrip(dangi, None, 400, "M02", 5);
1821        single_test_roundtrip(dangi, None, 4660, "M08", 29);
1822        single_test_roundtrip(dangi, None, -1300, "M11", 12);
1823        single_test_error(
1824            dangi,
1825            None,
1826            10393,
1827            "M00L",
1828            1,
1829            DateError::UnknownMonthCode(MonthCode(tinystr!(4, "M00L"))),
1830        );
1831
1832        single_test_roundtrip(japanese, Some(("reiwa", None)), 3, "M03", 1);
1833        single_test_roundtrip(japanese, Some(("heisei", None)), 6, "M12", 1);
1834        single_test_roundtrip(japanese, Some(("meiji", None)), 10, "M03", 1);
1835        single_test_roundtrip(japanese, Some(("ce", None)), 1000, "M03", 1);
1836        single_test_roundtrip(japanese, None, 1000, "M03", 1);
1837        single_test_roundtrip(japanese, None, -100, "M03", 1);
1838        single_test_roundtrip(japanese, None, 2024, "M03", 1);
1839        single_test_roundtrip(japanese, Some(("bce", None)), 10, "M03", 1);
1840        // Since #6910, the era range is not enforced in try_from_codes
1841        /*
1842        single_test_error(
1843            japanese,
1844            Some(("ce", None)),
1845            0,
1846            "M03",
1847            1,
1848            DateError::Range {
1849                field: "year",
1850                value: 0,
1851                min: 1,
1852                max: i32::MAX,
1853            },
1854        );
1855        single_test_error(
1856            japanese,
1857            Some(("bce", Some(0))),
1858            0,
1859            "M03",
1860            1,
1861            DateError::Range {
1862                field: "year",
1863                value: 0,
1864                min: 1,
1865                max: i32::MAX,
1866            },
1867        );
1868        */
1869        single_test_error(
1870            japanese,
1871            Some(("reiwa", None)),
1872            2,
1873            "M13",
1874            1,
1875            DateError::UnknownMonthCode(MonthCode(tinystr!(4, "M13"))),
1876        );
1877
1878        single_test_roundtrip(japanext, Some(("reiwa", None)), 3, "M03", 1);
1879        single_test_roundtrip(japanext, Some(("heisei", None)), 6, "M12", 1);
1880        single_test_roundtrip(japanext, Some(("meiji", None)), 10, "M03", 1);
1881        single_test_roundtrip(japanext, Some(("tenpyokampo-749", None)), 1, "M04", 20);
1882        single_test_roundtrip(japanext, Some(("ce", None)), 100, "M03", 1);
1883        single_test_roundtrip(japanext, Some(("bce", None)), 10, "M03", 1);
1884        // Since #6910, the era range is not enforced in try_from_codes
1885        /*
1886        single_test_error(
1887            japanext,
1888            Some(("ce", None)),
1889            0,
1890            "M03",
1891            1,
1892            DateError::Range {
1893                field: "year",
1894                value: 0,
1895                min: 1,
1896                max: i32::MAX,
1897            },
1898        );
1899        single_test_error(
1900            japanext,
1901            Some(("bce", Some(0))),
1902            0,
1903            "M03",
1904            1,
1905            DateError::Range {
1906                field: "year",
1907                value: 0,
1908                min: 1,
1909                max: i32::MAX,
1910            },
1911        );
1912        */
1913        single_test_error(
1914            japanext,
1915            Some(("reiwa", None)),
1916            2,
1917            "M13",
1918            1,
1919            DateError::UnknownMonthCode(MonthCode(tinystr!(4, "M13"))),
1920        );
1921
1922        single_test_roundtrip(persian, Some(("ap", Some(0))), 477, "M03", 1);
1923        single_test_roundtrip(persian, None, 2083, "M07", 21);
1924        single_test_roundtrip(persian, None, -100, "M07", 21);
1925        single_test_roundtrip(persian, Some(("ap", Some(0))), 1600, "M12", 20);
1926        single_test_error(
1927            persian,
1928            Some(("ap", Some(0))),
1929            100,
1930            "M9",
1931            1,
1932            DateError::UnknownMonthCode(MonthCode(tinystr!(4, "M9"))),
1933        );
1934
1935        single_test_roundtrip(hebrew, Some(("am", Some(0))), 5773, "M03", 1);
1936        single_test_roundtrip(hebrew, None, 4993, "M07", 21);
1937        single_test_roundtrip(hebrew, None, -100, "M07", 21);
1938        single_test_roundtrip(hebrew, Some(("am", Some(0))), 5012, "M12", 20);
1939        single_test_error(
1940            hebrew,
1941            Some(("am", Some(0))),
1942            100,
1943            "M9",
1944            1,
1945            DateError::UnknownMonthCode(MonthCode(tinystr!(4, "M9"))),
1946        );
1947
1948        single_test_roundtrip(roc, Some(("roc", Some(1))), 10, "M05", 3);
1949        single_test_roundtrip(roc, Some(("broc", Some(0))), 15, "M01", 10);
1950        single_test_roundtrip(roc, None, 100, "M10", 30);
1951        single_test_roundtrip(roc, None, -100, "M10", 30);
1952
1953        single_test_roundtrip(hijri_simulated, Some(("ah", Some(0))), 477, "M03", 1);
1954        single_test_roundtrip(hijri_simulated, None, 2083, "M07", 21);
1955        single_test_roundtrip(hijri_simulated, None, -100, "M07", 21);
1956        single_test_roundtrip(hijri_simulated, Some(("ah", Some(0))), 1600, "M12", 20);
1957        single_test_error(
1958            hijri_simulated,
1959            Some(("ah", Some(0))),
1960            100,
1961            "M9",
1962            1,
1963            DateError::UnknownMonthCode(MonthCode(tinystr!(4, "M9"))),
1964        );
1965
1966        single_test_roundtrip(hijri_civil, Some(("ah", Some(0))), 477, "M03", 1);
1967        single_test_roundtrip(hijri_civil, None, 2083, "M07", 21);
1968        single_test_roundtrip(hijri_civil, None, -100, "M07", 21);
1969        single_test_roundtrip(hijri_civil, Some(("ah", Some(0))), 1600, "M12", 20);
1970        single_test_error(
1971            hijri_civil,
1972            Some(("ah", Some(0))),
1973            100,
1974            "M9",
1975            1,
1976            DateError::UnknownMonthCode(MonthCode(tinystr!(4, "M9"))),
1977        );
1978
1979        single_test_roundtrip(hijri_umm_al_qura, Some(("ah", Some(0))), 477, "M03", 1);
1980        single_test_roundtrip(hijri_umm_al_qura, None, 2083, "M07", 21);
1981        single_test_roundtrip(hijri_umm_al_qura, None, -100, "M07", 21);
1982        single_test_roundtrip(hijri_umm_al_qura, Some(("ah", Some(0))), 1600, "M12", 20);
1983        single_test_error(
1984            hijri_umm_al_qura,
1985            Some(("ah", Some(0))),
1986            100,
1987            "M9",
1988            1,
1989            DateError::UnknownMonthCode(MonthCode(tinystr!(4, "M9"))),
1990        );
1991
1992        single_test_roundtrip(hijri_astronomical, Some(("ah", Some(0))), 477, "M03", 1);
1993        single_test_roundtrip(hijri_astronomical, None, 2083, "M07", 21);
1994        single_test_roundtrip(hijri_astronomical, None, -100, "M07", 21);
1995        single_test_roundtrip(hijri_astronomical, Some(("ah", Some(0))), 1600, "M12", 20);
1996        single_test_error(
1997            hijri_astronomical,
1998            Some(("ah", Some(0))),
1999            100,
2000            "M9",
2001            1,
2002            DateError::UnknownMonthCode(MonthCode(tinystr!(4, "M9"))),
2003        );
2004    }
2005}