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