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::buddhist::Buddhist;
8use crate::chinese::Chinese;
9use crate::coptic::Coptic;
10use crate::dangi::Dangi;
11use crate::ethiopian::{Ethiopian, EthiopianEraStyle};
12use crate::gregorian::Gregorian;
13use crate::hebrew::Hebrew;
14use crate::indian::Indian;
15use crate::islamic::{IslamicCivil, IslamicObservational, IslamicTabular, IslamicUmmAlQura};
16use crate::iso::Iso;
17use crate::japanese::{Japanese, JapaneseExtended};
18use crate::persian::Persian;
19use crate::roc::Roc;
20use crate::{
21    types, AsCalendar, Calendar, CalendarError, Date, DateDuration, DateDurationUnit, DateTime, Ref,
22};
23
24use icu_locid::extensions::unicode::{key, value, Value};
25use icu_locid::subtags::language;
26use icu_locid::Locale;
27use icu_provider::prelude::*;
28
29use core::fmt;
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, DateTime, japanese::Japanese, Time};
44/// use icu::locid::locale;
45/// # use std::rc::Rc;
46///
47/// let locale = locale!("en-u-ca-japanese"); // English with the Japanese calendar
48///
49/// let calendar = AnyCalendar::new_for_locale(&locale.into());
50/// let calendar = Rc::new(calendar); // Avoid cloning it each time
51///                                   // If everything is a local reference, you may use icu::calendar::Ref instead.
52///
53/// // manually construct a datetime in this calendar
54/// let manual_time = Time::try_new(12, 33, 12, 0).expect("failed to construct Time");
55/// // construct from era code, year, month code, day, time, and a calendar
56/// // This is March 28, 15 Heisei
57/// let manual_datetime = DateTime::try_new_from_codes("heisei".parse().unwrap(), 15, "M03".parse().unwrap(), 28,
58///                                                manual_time, calendar.clone())
59///                     .expect("Failed to construct DateTime manually");
60///
61///
62/// // construct another datetime by converting from ISO
63/// let iso_datetime = DateTime::try_new_iso_datetime(2020, 9, 1, 12, 34, 28)
64///     .expect("Failed to construct ISO DateTime.");
65/// let iso_converted = iso_datetime.to_calendar(calendar);
66///
67/// // Construct a datetime in the appropriate typed calendar and convert
68/// let japanese_calendar = Japanese::new();
69/// let japanese_datetime = DateTime::try_new_japanese_datetime("heisei".parse().unwrap(), 15, 3, 28,
70///                                                         12, 33, 12, japanese_calendar).unwrap();
71/// // This is a DateTime<AnyCalendar>
72/// let any_japanese_datetime = japanese_datetime.to_any();
73/// ```
74#[derive(Debug)]
75#[non_exhaustive]
76pub enum AnyCalendar {
77    /// A [`Buddhist`] calendar
78    Buddhist(Buddhist),
79    /// A [`Chinese`] calendar
80    Chinese(Chinese),
81    /// A [`Coptic`] calendar
82    Coptic(Coptic),
83    /// A [`Dangi`] calendar
84    Dangi(Dangi),
85    /// An [`Ethiopian`] calendar
86    Ethiopian(Ethiopian),
87    /// A [`Gregorian`] calendar
88    Gregorian(Gregorian),
89    /// A [`Hebrew`] calendar
90    Hebrew(Hebrew),
91    /// An [`Indian`] calendar
92    Indian(Indian),
93    /// An [`IslamicCivil`] calendar
94    IslamicCivil(IslamicCivil),
95    /// An [`IslamicObservational`] calendar
96    IslamicObservational(IslamicObservational),
97    /// An [`IslamicTabular`] calendar
98    IslamicTabular(IslamicTabular),
99    /// An [`IslamicUmmAlQura`] calendar
100    IslamicUmmAlQura(IslamicUmmAlQura),
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)]
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 an [`IslamicCivil`] calendar
135    IslamicCivil(<IslamicCivil as Calendar>::DateInner),
136    /// A date for an [`IslamicObservational`] calendar
137    IslamicObservational(<IslamicObservational as Calendar>::DateInner),
138    /// A date for an [`IslamicTabular`] calendar
139    IslamicTabular(<IslamicTabular as Calendar>::DateInner),
140    /// A date for an [`IslamicUmmAlQura`] calendar
141    IslamicUmmAlQura(<IslamicUmmAlQura as Calendar>::DateInner),
142    /// A date for an [`Iso`] calendar
143    Iso(<Iso as Calendar>::DateInner),
144    /// A date for a [`Japanese`] calendar
145    Japanese(<Japanese as Calendar>::DateInner),
146    /// A date for a [`JapaneseExtended`] calendar
147    JapaneseExtended(<JapaneseExtended as Calendar>::DateInner),
148    /// A date for a [`Persian`] calendar
149    Persian(<Persian as Calendar>::DateInner),
150    /// A date for a [`Roc`] calendar
151    Roc(<Roc as Calendar>::DateInner),
152}
153
154macro_rules! match_cal_and_date {
155    (match ($cal:ident, $date:ident): ($cal_matched:ident, $date_matched:ident) => $e:expr) => {
156        match ($cal, $date) {
157            (&Self::Buddhist(ref $cal_matched), &AnyDateInner::Buddhist(ref $date_matched)) => $e,
158            (&Self::Chinese(ref $cal_matched), &AnyDateInner::Chinese(ref $date_matched)) => $e,
159            (&Self::Coptic(ref $cal_matched), &AnyDateInner::Coptic(ref $date_matched)) => $e,
160            (&Self::Dangi(ref $cal_matched), &AnyDateInner::Dangi(ref $date_matched)) => $e,
161            (&Self::Ethiopian(ref $cal_matched), &AnyDateInner::Ethiopian(ref $date_matched)) => $e,
162            (&Self::Gregorian(ref $cal_matched), &AnyDateInner::Gregorian(ref $date_matched)) => $e,
163            (&Self::Hebrew(ref $cal_matched), &AnyDateInner::Hebrew(ref $date_matched)) => $e,
164            (&Self::Indian(ref $cal_matched), &AnyDateInner::Indian(ref $date_matched)) => $e,
165            (
166                &Self::IslamicCivil(ref $cal_matched),
167                &AnyDateInner::IslamicCivil(ref $date_matched),
168            ) => $e,
169            (
170                &Self::IslamicObservational(ref $cal_matched),
171                &AnyDateInner::IslamicObservational(ref $date_matched),
172            ) => $e,
173            (
174                &Self::IslamicTabular(ref $cal_matched),
175                &AnyDateInner::IslamicTabular(ref $date_matched),
176            ) => $e,
177            (
178                &Self::IslamicUmmAlQura(ref $cal_matched),
179                &AnyDateInner::IslamicUmmAlQura(ref $date_matched),
180            ) => $e,
181            (&Self::Iso(ref $cal_matched), &AnyDateInner::Iso(ref $date_matched)) => $e,
182            (&Self::Japanese(ref $cal_matched), &AnyDateInner::Japanese(ref $date_matched)) => $e,
183            (
184                &Self::JapaneseExtended(ref $cal_matched),
185                &AnyDateInner::JapaneseExtended(ref $date_matched),
186            ) => $e,
187            (&Self::Persian(ref $cal_matched), &AnyDateInner::Persian(ref $date_matched)) => $e,
188            (&Self::Roc(ref $cal_matched), &AnyDateInner::Roc(ref $date_matched)) => $e,
189            _ => panic!(
190                "Found AnyCalendar with mixed calendar type {:?} and date type {:?}!",
191                $cal.kind().debug_name(),
192                $date.kind().debug_name()
193            ),
194        }
195    };
196}
197
198impl Calendar for AnyCalendar {
199    type DateInner = AnyDateInner;
200    fn date_from_codes(
201        &self,
202        era: types::Era,
203        year: i32,
204        month_code: types::MonthCode,
205        day: u8,
206    ) -> Result<Self::DateInner, CalendarError> {
207        let ret = match *self {
208            Self::Buddhist(ref c) => {
209                AnyDateInner::Buddhist(c.date_from_codes(era, year, month_code, day)?)
210            }
211            Self::Chinese(ref c) => {
212                AnyDateInner::Chinese(c.date_from_codes(era, year, month_code, day)?)
213            }
214            Self::Coptic(ref c) => {
215                AnyDateInner::Coptic(c.date_from_codes(era, year, month_code, day)?)
216            }
217            Self::Dangi(ref c) => {
218                AnyDateInner::Dangi(c.date_from_codes(era, year, month_code, day)?)
219            }
220            Self::Ethiopian(ref c) => {
221                AnyDateInner::Ethiopian(c.date_from_codes(era, year, month_code, day)?)
222            }
223            Self::Gregorian(ref c) => {
224                AnyDateInner::Gregorian(c.date_from_codes(era, year, month_code, day)?)
225            }
226            Self::Hebrew(ref c) => {
227                AnyDateInner::Hebrew(c.date_from_codes(era, year, month_code, day)?)
228            }
229            Self::Indian(ref c) => {
230                AnyDateInner::Indian(c.date_from_codes(era, year, month_code, day)?)
231            }
232            Self::IslamicCivil(ref c) => {
233                AnyDateInner::IslamicCivil(c.date_from_codes(era, year, month_code, day)?)
234            }
235            Self::IslamicObservational(ref c) => {
236                AnyDateInner::IslamicObservational(c.date_from_codes(era, year, month_code, day)?)
237            }
238            Self::IslamicTabular(ref c) => {
239                AnyDateInner::IslamicTabular(c.date_from_codes(era, year, month_code, day)?)
240            }
241            Self::IslamicUmmAlQura(ref c) => {
242                AnyDateInner::IslamicUmmAlQura(c.date_from_codes(era, year, month_code, day)?)
243            }
244            Self::Iso(ref c) => AnyDateInner::Iso(c.date_from_codes(era, year, month_code, day)?),
245            Self::Japanese(ref c) => {
246                AnyDateInner::Japanese(c.date_from_codes(era, year, month_code, day)?)
247            }
248            Self::JapaneseExtended(ref c) => {
249                AnyDateInner::JapaneseExtended(c.date_from_codes(era, year, month_code, day)?)
250            }
251            Self::Persian(ref c) => {
252                AnyDateInner::Persian(c.date_from_codes(era, year, month_code, day)?)
253            }
254            Self::Roc(ref c) => AnyDateInner::Roc(c.date_from_codes(era, year, month_code, day)?),
255        };
256        Ok(ret)
257    }
258    fn date_from_iso(&self, iso: Date<Iso>) -> AnyDateInner {
259        match *self {
260            Self::Buddhist(ref c) => AnyDateInner::Buddhist(c.date_from_iso(iso)),
261            Self::Chinese(ref c) => AnyDateInner::Chinese(c.date_from_iso(iso)),
262            Self::Coptic(ref c) => AnyDateInner::Coptic(c.date_from_iso(iso)),
263            Self::Dangi(ref c) => AnyDateInner::Dangi(c.date_from_iso(iso)),
264            Self::Ethiopian(ref c) => AnyDateInner::Ethiopian(c.date_from_iso(iso)),
265            Self::Gregorian(ref c) => AnyDateInner::Gregorian(c.date_from_iso(iso)),
266            Self::Hebrew(ref c) => AnyDateInner::Hebrew(c.date_from_iso(iso)),
267            Self::Indian(ref c) => AnyDateInner::Indian(c.date_from_iso(iso)),
268            Self::IslamicCivil(ref c) => AnyDateInner::IslamicCivil(c.date_from_iso(iso)),
269            Self::IslamicObservational(ref c) => {
270                AnyDateInner::IslamicObservational(c.date_from_iso(iso))
271            }
272            Self::IslamicTabular(ref c) => AnyDateInner::IslamicTabular(c.date_from_iso(iso)),
273            Self::IslamicUmmAlQura(ref c) => AnyDateInner::IslamicUmmAlQura(c.date_from_iso(iso)),
274            Self::Iso(ref c) => AnyDateInner::Iso(c.date_from_iso(iso)),
275            Self::Japanese(ref c) => AnyDateInner::Japanese(c.date_from_iso(iso)),
276            Self::JapaneseExtended(ref c) => AnyDateInner::JapaneseExtended(c.date_from_iso(iso)),
277            Self::Persian(ref c) => AnyDateInner::Persian(c.date_from_iso(iso)),
278            Self::Roc(ref c) => AnyDateInner::Roc(c.date_from_iso(iso)),
279        }
280    }
281
282    fn date_to_iso(&self, date: &Self::DateInner) -> Date<Iso> {
283        match_cal_and_date!(match (self, date): (c, d) => c.date_to_iso(d))
284    }
285
286    fn months_in_year(&self, date: &Self::DateInner) -> u8 {
287        match_cal_and_date!(match (self, date): (c, d) => c.months_in_year(d))
288    }
289
290    fn days_in_year(&self, date: &Self::DateInner) -> u16 {
291        match_cal_and_date!(match (self, date): (c, d) => c.days_in_year(d))
292    }
293
294    fn days_in_month(&self, date: &Self::DateInner) -> u8 {
295        match_cal_and_date!(match (self, date): (c, d) => c.days_in_month(d))
296    }
297
298    fn offset_date(&self, date: &mut Self::DateInner, offset: DateDuration<Self>) {
299        match (self, date) {
300            (Self::Buddhist(c), &mut AnyDateInner::Buddhist(ref mut d)) => {
301                c.offset_date(d, offset.cast_unit())
302            }
303            (Self::Chinese(c), &mut AnyDateInner::Chinese(ref mut d)) => {
304                c.offset_date(d, offset.cast_unit())
305            }
306            (Self::Coptic(c), &mut AnyDateInner::Coptic(ref mut d)) => {
307                c.offset_date(d, offset.cast_unit())
308            }
309            (Self::Dangi(c), &mut AnyDateInner::Dangi(ref mut d)) => {
310                c.offset_date(d, offset.cast_unit())
311            }
312            (Self::Ethiopian(c), &mut AnyDateInner::Ethiopian(ref mut d)) => {
313                c.offset_date(d, offset.cast_unit())
314            }
315            (Self::Gregorian(c), &mut AnyDateInner::Gregorian(ref mut d)) => {
316                c.offset_date(d, offset.cast_unit())
317            }
318            (Self::Hebrew(c), &mut AnyDateInner::Hebrew(ref mut d)) => {
319                c.offset_date(d, offset.cast_unit())
320            }
321            (Self::Indian(c), &mut AnyDateInner::Indian(ref mut d)) => {
322                c.offset_date(d, offset.cast_unit())
323            }
324            (Self::IslamicCivil(c), &mut AnyDateInner::IslamicCivil(ref mut d)) => {
325                c.offset_date(d, offset.cast_unit())
326            }
327            (Self::IslamicObservational(c), &mut AnyDateInner::IslamicObservational(ref mut d)) => {
328                c.offset_date(d, offset.cast_unit())
329            }
330            (Self::IslamicTabular(c), &mut AnyDateInner::IslamicTabular(ref mut d)) => {
331                c.offset_date(d, offset.cast_unit())
332            }
333            (Self::IslamicUmmAlQura(c), &mut AnyDateInner::IslamicUmmAlQura(ref mut d)) => {
334                c.offset_date(d, offset.cast_unit())
335            }
336            (Self::Iso(c), &mut AnyDateInner::Iso(ref mut d)) => {
337                c.offset_date(d, offset.cast_unit())
338            }
339            (Self::Japanese(c), &mut AnyDateInner::Japanese(ref mut d)) => {
340                c.offset_date(d, offset.cast_unit())
341            }
342            (Self::JapaneseExtended(c), &mut AnyDateInner::JapaneseExtended(ref mut d)) => {
343                c.offset_date(d, offset.cast_unit())
344            }
345            (Self::Persian(c), &mut AnyDateInner::Persian(ref mut d)) => {
346                c.offset_date(d, offset.cast_unit())
347            }
348            (Self::Roc(c), &mut AnyDateInner::Roc(ref mut d)) => {
349                c.offset_date(d, offset.cast_unit())
350            }
351            // This is only reached from misuse of from_raw, a semi-internal api
352            #[allow(clippy::panic)]
353            (_, d) => panic!(
354                "Found AnyCalendar with mixed calendar type {} and date type {}!",
355                self.kind().debug_name(),
356                d.kind().debug_name()
357            ),
358        }
359    }
360
361    fn until(
362        &self,
363        date1: &Self::DateInner,
364        date2: &Self::DateInner,
365        calendar2: &Self,
366        largest_unit: DateDurationUnit,
367        smallest_unit: DateDurationUnit,
368    ) -> DateDuration<Self> {
369        match (self, calendar2, date1, date2) {
370            (
371                Self::Buddhist(c1),
372                Self::Buddhist(c2),
373                AnyDateInner::Buddhist(d1),
374                AnyDateInner::Buddhist(d2),
375            ) => c1
376                .until(d1, d2, c2, largest_unit, smallest_unit)
377                .cast_unit(),
378            (
379                Self::Chinese(c1),
380                Self::Chinese(c2),
381                AnyDateInner::Chinese(d1),
382                AnyDateInner::Chinese(d2),
383            ) => c1
384                .until(d1, d2, c2, largest_unit, smallest_unit)
385                .cast_unit(),
386            (
387                Self::Coptic(c1),
388                Self::Coptic(c2),
389                AnyDateInner::Coptic(d1),
390                AnyDateInner::Coptic(d2),
391            ) => c1
392                .until(d1, d2, c2, largest_unit, smallest_unit)
393                .cast_unit(),
394            (
395                Self::Dangi(c1),
396                Self::Dangi(c2),
397                AnyDateInner::Dangi(d1),
398                AnyDateInner::Dangi(d2),
399            ) => c1
400                .until(d1, d2, c2, largest_unit, smallest_unit)
401                .cast_unit(),
402            (
403                Self::Ethiopian(c1),
404                Self::Ethiopian(c2),
405                AnyDateInner::Ethiopian(d1),
406                AnyDateInner::Ethiopian(d2),
407            ) => c1
408                .until(d1, d2, c2, largest_unit, smallest_unit)
409                .cast_unit(),
410            (
411                Self::Gregorian(c1),
412                Self::Gregorian(c2),
413                AnyDateInner::Gregorian(d1),
414                AnyDateInner::Gregorian(d2),
415            ) => c1
416                .until(d1, d2, c2, largest_unit, smallest_unit)
417                .cast_unit(),
418            (
419                Self::Hebrew(c1),
420                Self::Hebrew(c2),
421                AnyDateInner::Hebrew(d1),
422                AnyDateInner::Hebrew(d2),
423            ) => c1
424                .until(d1, d2, c2, largest_unit, smallest_unit)
425                .cast_unit(),
426            (
427                Self::Indian(c1),
428                Self::Indian(c2),
429                AnyDateInner::Indian(d1),
430                AnyDateInner::Indian(d2),
431            ) => c1
432                .until(d1, d2, c2, largest_unit, smallest_unit)
433                .cast_unit(),
434            (
435                Self::IslamicCivil(c1),
436                Self::IslamicCivil(c2),
437                AnyDateInner::IslamicCivil(d1),
438                AnyDateInner::IslamicCivil(d2),
439            ) => c1
440                .until(d1, d2, c2, largest_unit, smallest_unit)
441                .cast_unit(),
442            (
443                Self::IslamicObservational(c1),
444                Self::IslamicObservational(c2),
445                AnyDateInner::IslamicObservational(d1),
446                AnyDateInner::IslamicObservational(d2),
447            ) => c1
448                .until(d1, d2, c2, largest_unit, smallest_unit)
449                .cast_unit(),
450            (
451                Self::IslamicTabular(c1),
452                Self::IslamicTabular(c2),
453                AnyDateInner::IslamicTabular(d1),
454                AnyDateInner::IslamicTabular(d2),
455            ) => c1
456                .until(d1, d2, c2, largest_unit, smallest_unit)
457                .cast_unit(),
458            (
459                Self::IslamicUmmAlQura(c1),
460                Self::IslamicUmmAlQura(c2),
461                AnyDateInner::IslamicUmmAlQura(d1),
462                AnyDateInner::IslamicUmmAlQura(d2),
463            ) => c1
464                .until(d1, d2, c2, largest_unit, smallest_unit)
465                .cast_unit(),
466            (Self::Iso(c1), Self::Iso(c2), AnyDateInner::Iso(d1), AnyDateInner::Iso(d2)) => c1
467                .until(d1, d2, c2, largest_unit, smallest_unit)
468                .cast_unit(),
469            (
470                Self::Japanese(c1),
471                Self::Japanese(c2),
472                AnyDateInner::Japanese(d1),
473                AnyDateInner::Japanese(d2),
474            ) => c1
475                .until(d1, d2, c2, largest_unit, smallest_unit)
476                .cast_unit(),
477            (
478                Self::JapaneseExtended(c1),
479                Self::JapaneseExtended(c2),
480                AnyDateInner::JapaneseExtended(d1),
481                AnyDateInner::JapaneseExtended(d2),
482            ) => c1
483                .until(d1, d2, c2, largest_unit, smallest_unit)
484                .cast_unit(),
485            (
486                Self::Persian(c1),
487                Self::Persian(c2),
488                AnyDateInner::Persian(d1),
489                AnyDateInner::Persian(d2),
490            ) => c1
491                .until(d1, d2, c2, largest_unit, smallest_unit)
492                .cast_unit(),
493            (Self::Roc(c1), Self::Roc(c2), AnyDateInner::Roc(d1), AnyDateInner::Roc(d2)) => c1
494                .until(d1, d2, c2, largest_unit, smallest_unit)
495                .cast_unit(),
496            _ => {
497                // attempt to convert
498                let iso = calendar2.date_to_iso(date2);
499
500                match_cal_and_date!(match (self, date1):
501                    (c1, d1) => {
502                        let d2 = c1.date_from_iso(iso);
503                        let until = c1.until(d1, &d2, c1, largest_unit, smallest_unit);
504                        until.cast_unit::<AnyCalendar>()
505                    }
506                )
507            }
508        }
509    }
510
511    /// The calendar-specific year represented by `date`
512    fn year(&self, date: &Self::DateInner) -> types::FormattableYear {
513        match_cal_and_date!(match (self, date): (c, d) => c.year(d))
514    }
515
516    /// The calendar-specific check if `date` is in a leap year
517    fn is_in_leap_year(&self, date: &Self::DateInner) -> bool {
518        match_cal_and_date!(match (self, date): (c, d) => c.is_in_leap_year(d))
519    }
520
521    /// The calendar-specific month represented by `date`
522    fn month(&self, date: &Self::DateInner) -> types::FormattableMonth {
523        match_cal_and_date!(match (self, date): (c, d) => c.month(d))
524    }
525
526    /// The calendar-specific day-of-month represented by `date`
527    fn day_of_month(&self, date: &Self::DateInner) -> types::DayOfMonth {
528        match_cal_and_date!(match (self, date): (c, d) => c.day_of_month(d))
529    }
530
531    /// Information of the day of the year
532    fn day_of_year_info(&self, date: &Self::DateInner) -> types::DayOfYearInfo {
533        match_cal_and_date!(match (self, date): (c, d) => c.day_of_year_info(d))
534    }
535
536    fn debug_name(&self) -> &'static str {
537        match *self {
538            Self::Buddhist(_) => "AnyCalendar (Buddhist)",
539            Self::Chinese(_) => "AnyCalendar (Chinese)",
540            Self::Coptic(_) => "AnyCalendar (Coptic)",
541            Self::Dangi(_) => "AnyCalendar (Dangi)",
542            Self::Ethiopian(_) => "AnyCalendar (Ethiopian)",
543            Self::Gregorian(_) => "AnyCalendar (Gregorian)",
544            Self::Hebrew(_) => "AnyCalendar (Hebrew)",
545            Self::Indian(_) => "AnyCalendar (Indian)",
546            Self::IslamicCivil(_) => "AnyCalendar (Islamic, civil)",
547            Self::IslamicObservational(_) => "AnyCalendar (Islamic, observational)",
548            Self::IslamicTabular(_) => "AnyCalendar (Islamic, tabular)",
549            Self::IslamicUmmAlQura(_) => "AnyCalendar (Islamic, Umm al-Qura)",
550            Self::Iso(_) => "AnyCalendar (Iso)",
551            Self::Japanese(_) => "AnyCalendar (Japanese)",
552            Self::JapaneseExtended(_) => "AnyCalendar (Japanese, historical era data)",
553            Self::Persian(_) => "AnyCalendar (Persian)",
554            Self::Roc(_) => "AnyCalendar (Roc)",
555        }
556    }
557
558    fn any_calendar_kind(&self) -> Option<AnyCalendarKind> {
559        Some(self.kind())
560    }
561}
562
563impl AnyCalendar {
564    /// Constructs an AnyCalendar for a given calendar kind from compiled data.
565    ///
566    /// As this requires a valid [`AnyCalendarKind`] to work, it does not do any kind of locale-based
567    /// fallbacking. If this is desired, use [`Self::new_for_locale()`].
568    ///
569    /// ✨ *Enabled with the `compiled_data` Cargo feature.*
570    ///
571    /// [📚 Help choosing a constructor](icu_provider::constructors)
572    #[cfg(feature = "compiled_data")]
573    pub const fn new(kind: AnyCalendarKind) -> Self {
574        match kind {
575            AnyCalendarKind::Buddhist => AnyCalendar::Buddhist(Buddhist),
576            AnyCalendarKind::Chinese => AnyCalendar::Chinese(Chinese::new()),
577            AnyCalendarKind::Coptic => AnyCalendar::Coptic(Coptic),
578            AnyCalendarKind::Dangi => AnyCalendar::Dangi(Dangi::new()),
579            AnyCalendarKind::Ethiopian => AnyCalendar::Ethiopian(Ethiopian::new_with_era_style(
580                EthiopianEraStyle::AmeteMihret,
581            )),
582            AnyCalendarKind::EthiopianAmeteAlem => {
583                AnyCalendar::Ethiopian(Ethiopian::new_with_era_style(EthiopianEraStyle::AmeteAlem))
584            }
585            AnyCalendarKind::Gregorian => AnyCalendar::Gregorian(Gregorian),
586            AnyCalendarKind::Hebrew => AnyCalendar::Hebrew(Hebrew),
587            AnyCalendarKind::Indian => AnyCalendar::Indian(Indian),
588            AnyCalendarKind::IslamicCivil => AnyCalendar::IslamicCivil(IslamicCivil),
589            AnyCalendarKind::IslamicObservational => {
590                AnyCalendar::IslamicObservational(IslamicObservational::new())
591            }
592            AnyCalendarKind::IslamicTabular => AnyCalendar::IslamicTabular(IslamicTabular),
593            AnyCalendarKind::IslamicUmmAlQura => {
594                AnyCalendar::IslamicUmmAlQura(IslamicUmmAlQura::new())
595            }
596            AnyCalendarKind::Iso => AnyCalendar::Iso(Iso),
597            AnyCalendarKind::Japanese => AnyCalendar::Japanese(Japanese::new()),
598            AnyCalendarKind::JapaneseExtended => {
599                AnyCalendar::JapaneseExtended(JapaneseExtended::new())
600            }
601            AnyCalendarKind::Persian => AnyCalendar::Persian(Persian),
602            AnyCalendarKind::Roc => AnyCalendar::Roc(Roc),
603        }
604    }
605
606    #[doc = icu_provider::gen_any_buffer_unstable_docs!(ANY, Self::new)]
607    pub fn try_new_with_any_provider<P>(
608        provider: &P,
609        kind: AnyCalendarKind,
610    ) -> Result<Self, CalendarError>
611    where
612        P: AnyProvider + ?Sized,
613    {
614        Ok(match kind {
615            AnyCalendarKind::Buddhist => AnyCalendar::Buddhist(Buddhist),
616            AnyCalendarKind::Chinese => {
617                AnyCalendar::Chinese(Chinese::try_new_with_any_provider(provider)?)
618            }
619            AnyCalendarKind::Coptic => AnyCalendar::Coptic(Coptic),
620            AnyCalendarKind::Dangi => {
621                AnyCalendar::Dangi(Dangi::try_new_with_any_provider(provider)?)
622            }
623            AnyCalendarKind::Ethiopian => AnyCalendar::Ethiopian(Ethiopian::new_with_era_style(
624                EthiopianEraStyle::AmeteMihret,
625            )),
626            AnyCalendarKind::EthiopianAmeteAlem => {
627                AnyCalendar::Ethiopian(Ethiopian::new_with_era_style(EthiopianEraStyle::AmeteAlem))
628            }
629            AnyCalendarKind::Gregorian => AnyCalendar::Gregorian(Gregorian),
630            AnyCalendarKind::Hebrew => AnyCalendar::Hebrew(Hebrew),
631            AnyCalendarKind::Indian => AnyCalendar::Indian(Indian),
632            AnyCalendarKind::IslamicCivil => AnyCalendar::IslamicCivil(IslamicCivil),
633            AnyCalendarKind::IslamicObservational => AnyCalendar::IslamicObservational(
634                IslamicObservational::try_new_with_any_provider(provider)?,
635            ),
636            AnyCalendarKind::IslamicTabular => AnyCalendar::IslamicTabular(IslamicTabular),
637            AnyCalendarKind::IslamicUmmAlQura => AnyCalendar::IslamicUmmAlQura(
638                IslamicUmmAlQura::try_new_with_any_provider(provider)?,
639            ),
640            AnyCalendarKind::Iso => AnyCalendar::Iso(Iso),
641            AnyCalendarKind::Japanese => {
642                AnyCalendar::Japanese(Japanese::try_new_with_any_provider(provider)?)
643            }
644            AnyCalendarKind::JapaneseExtended => AnyCalendar::JapaneseExtended(
645                JapaneseExtended::try_new_with_any_provider(provider)?,
646            ),
647            AnyCalendarKind::Persian => AnyCalendar::Persian(Persian),
648            AnyCalendarKind::Roc => AnyCalendar::Roc(Roc),
649        })
650    }
651
652    #[cfg(feature = "serde")]
653    #[doc = icu_provider::gen_any_buffer_unstable_docs!(BUFFER, Self::new)]
654    pub fn try_new_with_buffer_provider<P>(
655        provider: &P,
656        kind: AnyCalendarKind,
657    ) -> Result<Self, CalendarError>
658    where
659        P: BufferProvider + ?Sized,
660    {
661        Ok(match kind {
662            AnyCalendarKind::Buddhist => AnyCalendar::Buddhist(Buddhist),
663            AnyCalendarKind::Chinese => {
664                AnyCalendar::Chinese(Chinese::try_new_with_buffer_provider(provider)?)
665            }
666            AnyCalendarKind::Coptic => AnyCalendar::Coptic(Coptic),
667            AnyCalendarKind::Dangi => {
668                AnyCalendar::Dangi(Dangi::try_new_with_buffer_provider(provider)?)
669            }
670            AnyCalendarKind::Ethiopian => AnyCalendar::Ethiopian(Ethiopian::new_with_era_style(
671                EthiopianEraStyle::AmeteMihret,
672            )),
673            AnyCalendarKind::EthiopianAmeteAlem => {
674                AnyCalendar::Ethiopian(Ethiopian::new_with_era_style(EthiopianEraStyle::AmeteAlem))
675            }
676            AnyCalendarKind::Gregorian => AnyCalendar::Gregorian(Gregorian),
677            AnyCalendarKind::Hebrew => AnyCalendar::Hebrew(Hebrew),
678            AnyCalendarKind::Indian => AnyCalendar::Indian(Indian),
679            AnyCalendarKind::IslamicCivil => AnyCalendar::IslamicCivil(IslamicCivil),
680            AnyCalendarKind::IslamicObservational => AnyCalendar::IslamicObservational(
681                IslamicObservational::try_new_with_buffer_provider(provider)?,
682            ),
683            AnyCalendarKind::IslamicTabular => AnyCalendar::IslamicTabular(IslamicTabular),
684            AnyCalendarKind::IslamicUmmAlQura => AnyCalendar::IslamicUmmAlQura(
685                IslamicUmmAlQura::try_new_with_buffer_provider(provider)?,
686            ),
687            AnyCalendarKind::Iso => AnyCalendar::Iso(Iso),
688            AnyCalendarKind::Japanese => {
689                AnyCalendar::Japanese(Japanese::try_new_with_buffer_provider(provider)?)
690            }
691            AnyCalendarKind::JapaneseExtended => AnyCalendar::JapaneseExtended(
692                JapaneseExtended::try_new_with_buffer_provider(provider)?,
693            ),
694            AnyCalendarKind::Persian => AnyCalendar::Persian(Persian),
695            AnyCalendarKind::Roc => AnyCalendar::Roc(Roc),
696        })
697    }
698
699    #[doc = icu_provider::gen_any_buffer_unstable_docs!(UNSTABLE, Self::new)]
700    pub fn try_new_unstable<P>(provider: &P, kind: AnyCalendarKind) -> Result<Self, CalendarError>
701    where
702        P: DataProvider<crate::provider::JapaneseErasV1Marker>
703            + DataProvider<crate::provider::JapaneseExtendedErasV1Marker>
704            + DataProvider<crate::provider::ChineseCacheV1Marker>
705            + DataProvider<crate::provider::DangiCacheV1Marker>
706            + DataProvider<crate::provider::IslamicObservationalCacheV1Marker>
707            + DataProvider<crate::provider::IslamicUmmAlQuraCacheV1Marker>
708            + ?Sized,
709    {
710        Ok(match kind {
711            AnyCalendarKind::Buddhist => AnyCalendar::Buddhist(Buddhist),
712            AnyCalendarKind::Chinese => AnyCalendar::Chinese(Chinese::try_new_unstable(provider)?),
713            AnyCalendarKind::Coptic => AnyCalendar::Coptic(Coptic),
714            AnyCalendarKind::Dangi => AnyCalendar::Dangi(Dangi::try_new_unstable(provider)?),
715            AnyCalendarKind::Ethiopian => AnyCalendar::Ethiopian(Ethiopian::new_with_era_style(
716                EthiopianEraStyle::AmeteMihret,
717            )),
718            AnyCalendarKind::EthiopianAmeteAlem => {
719                AnyCalendar::Ethiopian(Ethiopian::new_with_era_style(EthiopianEraStyle::AmeteAlem))
720            }
721            AnyCalendarKind::Gregorian => AnyCalendar::Gregorian(Gregorian),
722            AnyCalendarKind::Hebrew => AnyCalendar::Hebrew(Hebrew),
723            AnyCalendarKind::Indian => AnyCalendar::Indian(Indian),
724            AnyCalendarKind::IslamicCivil => AnyCalendar::IslamicCivil(IslamicCivil),
725            AnyCalendarKind::IslamicObservational => {
726                AnyCalendar::IslamicObservational(IslamicObservational::try_new_unstable(provider)?)
727            }
728            AnyCalendarKind::IslamicTabular => AnyCalendar::IslamicTabular(IslamicTabular),
729            AnyCalendarKind::IslamicUmmAlQura => {
730                AnyCalendar::IslamicUmmAlQura(IslamicUmmAlQura::try_new_unstable(provider)?)
731            }
732            AnyCalendarKind::Iso => AnyCalendar::Iso(Iso),
733            AnyCalendarKind::Japanese => {
734                AnyCalendar::Japanese(Japanese::try_new_unstable(provider)?)
735            }
736            AnyCalendarKind::JapaneseExtended => {
737                AnyCalendar::JapaneseExtended(JapaneseExtended::try_new_unstable(provider)?)
738            }
739            AnyCalendarKind::Persian => AnyCalendar::Persian(Persian),
740            AnyCalendarKind::Roc => AnyCalendar::Roc(Roc),
741        })
742    }
743
744    /// Constructs an AnyCalendar for a given calendar kind from compiled data.
745    ///
746    /// In case the locale's calendar is unknown or unspecified, it will attempt to load the default
747    /// calendar for the locale, falling back to gregorian.
748    ///
749    /// ✨ *Enabled with the `compiled_data` Cargo feature.*
750    ///
751    /// [📚 Help choosing a constructor](icu_provider::constructors)
752    #[cfg(feature = "compiled_data")]
753    pub fn new_for_locale(locale: &DataLocale) -> Self {
754        let kind = AnyCalendarKind::from_data_locale_with_fallback(locale);
755        Self::new(kind)
756    }
757
758    icu_provider::gen_any_buffer_data_constructors!(
759        locale: include,
760        options: skip,
761        error: CalendarError,
762        #[cfg(skip)]
763        functions: [
764            new_for_locale,
765            try_new_for_locale_with_any_provider,
766            try_new_for_locale_with_buffer_provider,
767            try_new_for_locale_unstable,
768            Self,
769        ]
770    );
771
772    #[doc = icu_provider::gen_any_buffer_unstable_docs!(UNSTABLE, Self::new_for_locale)]
773    pub fn try_new_for_locale_unstable<P>(
774        provider: &P,
775        locale: &DataLocale,
776    ) -> Result<Self, CalendarError>
777    where
778        P: DataProvider<crate::provider::JapaneseErasV1Marker>
779            + DataProvider<crate::provider::JapaneseExtendedErasV1Marker>
780            + DataProvider<crate::provider::ChineseCacheV1Marker>
781            + DataProvider<crate::provider::DangiCacheV1Marker>
782            + DataProvider<crate::provider::IslamicObservationalCacheV1Marker>
783            + DataProvider<crate::provider::IslamicUmmAlQuraCacheV1Marker>
784            + ?Sized,
785    {
786        let kind = AnyCalendarKind::from_data_locale_with_fallback(locale);
787        Self::try_new_unstable(provider, kind)
788    }
789
790    /// The [`AnyCalendarKind`] corresponding to the calendar this contains
791    pub fn kind(&self) -> AnyCalendarKind {
792        match *self {
793            Self::Buddhist(_) => AnyCalendarKind::Buddhist,
794            Self::Chinese(_) => AnyCalendarKind::Chinese,
795            Self::Coptic(_) => AnyCalendarKind::Coptic,
796            Self::Dangi(_) => AnyCalendarKind::Dangi,
797            #[allow(clippy::expect_used)] // Invariant known at compile time
798            Self::Ethiopian(ref e) => e
799                .any_calendar_kind()
800                .expect("Ethiopian calendar known to have an AnyCalendarKind"),
801            Self::Gregorian(_) => AnyCalendarKind::Gregorian,
802            Self::Hebrew(_) => AnyCalendarKind::Hebrew,
803            Self::Indian(_) => AnyCalendarKind::Indian,
804            Self::IslamicCivil(_) => AnyCalendarKind::IslamicCivil,
805            Self::IslamicObservational(_) => AnyCalendarKind::IslamicObservational,
806            Self::IslamicTabular(_) => AnyCalendarKind::IslamicTabular,
807            Self::IslamicUmmAlQura(_) => AnyCalendarKind::IslamicUmmAlQura,
808            Self::Iso(_) => AnyCalendarKind::Iso,
809            Self::Japanese(_) => AnyCalendarKind::Japanese,
810            Self::JapaneseExtended(_) => AnyCalendarKind::JapaneseExtended,
811            Self::Persian(_) => AnyCalendarKind::Persian,
812            Self::Roc(_) => AnyCalendarKind::Roc,
813        }
814    }
815
816    /// Given an AnyCalendar date, convert that date to another AnyCalendar date in this calendar,
817    /// if conversion is needed
818    pub fn convert_any_date<'a>(
819        &'a self,
820        date: &Date<impl AsCalendar<Calendar = AnyCalendar>>,
821    ) -> Date<Ref<'a, AnyCalendar>> {
822        if self.kind() != date.calendar.as_calendar().kind() {
823            Date::new_from_iso(date.to_iso(), Ref(self))
824        } else {
825            Date {
826                inner: date.inner.clone(),
827                calendar: Ref(self),
828            }
829        }
830    }
831
832    /// Given an AnyCalendar datetime, convert that date to another AnyCalendar datetime in this calendar,
833    /// if conversion is needed
834    pub fn convert_any_datetime<'a>(
835        &'a self,
836        date: &DateTime<impl AsCalendar<Calendar = AnyCalendar>>,
837    ) -> DateTime<Ref<'a, AnyCalendar>> {
838        DateTime {
839            time: date.time,
840            date: self.convert_any_date(&date.date),
841        }
842    }
843}
844
845impl AnyDateInner {
846    fn kind(&self) -> AnyCalendarKind {
847        match *self {
848            AnyDateInner::Buddhist(_) => AnyCalendarKind::Buddhist,
849            AnyDateInner::Chinese(_) => AnyCalendarKind::Chinese,
850            AnyDateInner::Coptic(_) => AnyCalendarKind::Coptic,
851            AnyDateInner::Dangi(_) => AnyCalendarKind::Dangi,
852            AnyDateInner::Ethiopian(_) => AnyCalendarKind::Ethiopian,
853            AnyDateInner::Gregorian(_) => AnyCalendarKind::Gregorian,
854            AnyDateInner::Hebrew(_) => AnyCalendarKind::Hebrew,
855            AnyDateInner::Indian(_) => AnyCalendarKind::Indian,
856            AnyDateInner::IslamicCivil(_) => AnyCalendarKind::IslamicCivil,
857            AnyDateInner::IslamicObservational(_) => AnyCalendarKind::IslamicObservational,
858            AnyDateInner::IslamicTabular(_) => AnyCalendarKind::IslamicTabular,
859            AnyDateInner::IslamicUmmAlQura(_) => AnyCalendarKind::IslamicUmmAlQura,
860            AnyDateInner::Iso(_) => AnyCalendarKind::Iso,
861            AnyDateInner::Japanese(_) => AnyCalendarKind::Japanese,
862            AnyDateInner::JapaneseExtended(_) => AnyCalendarKind::JapaneseExtended,
863            AnyDateInner::Persian(_) => AnyCalendarKind::Persian,
864            AnyDateInner::Roc(_) => AnyCalendarKind::Roc,
865        }
866    }
867}
868
869/// Convenient type for selecting the kind of AnyCalendar to construct
870#[non_exhaustive]
871#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
872pub enum AnyCalendarKind {
873    /// The kind of a [`Buddhist`] calendar
874    Buddhist,
875    /// The kind of a [`Chinese`] calendar
876    Chinese,
877    /// The kind of a [`Coptic`] calendar
878    Coptic,
879    /// The kind of a [`Dangi`] calendar
880    Dangi,
881    /// The kind of an [`Ethiopian`] calendar, with Amete Mihret era
882    Ethiopian,
883    /// The kind of an [`Ethiopian`] calendar, with Amete Alem era
884    EthiopianAmeteAlem,
885    /// The kind of a [`Gregorian`] calendar
886    Gregorian,
887    /// The kind of a [`Hebrew`] calendar
888    Hebrew,
889    /// The kind of a [`Indian`] calendar
890    Indian,
891    /// The kind of an [`IslamicCivil`] calendar
892    IslamicCivil,
893    /// The kind of an [`IslamicObservational`] calendar
894    IslamicObservational,
895    /// The kind of an [`IslamicTabular`] calendar
896    IslamicTabular,
897    /// The kind of an [`IslamicUmmAlQura`] calendar
898    IslamicUmmAlQura,
899    /// The kind of an [`Iso`] calendar
900    Iso,
901    /// The kind of a [`Japanese`] calendar
902    Japanese,
903    /// The kind of a [`JapaneseExtended`] calendar
904    JapaneseExtended,
905    /// The kind of a [`Persian`] calendar
906    Persian,
907    /// The kind of a [`Roc`] calendar
908    Roc,
909}
910
911impl AnyCalendarKind {
912    /// Construct from a BCP-47 string
913    ///
914    /// Returns `None` if the calendar is unknown. If you prefer an error, use
915    /// [`CalendarError::unknown_any_calendar_kind`].
916    pub fn get_for_bcp47_string(x: &str) -> Option<Self> {
917        Self::get_for_bcp47_bytes(x.as_bytes())
918    }
919    /// Construct from a BCP-47 byte string
920    ///
921    /// Returns `None` if the calendar is unknown. If you prefer an error, use
922    /// [`CalendarError::unknown_any_calendar_kind`].
923    pub fn get_for_bcp47_bytes(x: &[u8]) -> Option<Self> {
924        Some(match x {
925            b"buddhist" => AnyCalendarKind::Buddhist,
926            b"chinese" => AnyCalendarKind::Chinese,
927            b"coptic" => AnyCalendarKind::Coptic,
928            b"dangi" => AnyCalendarKind::Dangi,
929            b"ethioaa" => AnyCalendarKind::EthiopianAmeteAlem,
930            b"ethiopic" => AnyCalendarKind::Ethiopian,
931            b"gregory" => AnyCalendarKind::Gregorian,
932            b"hebrew" => AnyCalendarKind::Hebrew,
933            b"indian" => AnyCalendarKind::Indian,
934            b"islamic-civil" | b"islamicc" => AnyCalendarKind::IslamicCivil,
935            b"islamic-tbla" => AnyCalendarKind::IslamicTabular,
936            b"islamic-umalqura" => AnyCalendarKind::IslamicUmmAlQura,
937            b"islamic" => AnyCalendarKind::IslamicObservational,
938            b"iso" => AnyCalendarKind::Iso,
939            b"japanese" => AnyCalendarKind::Japanese,
940            b"japanext" => AnyCalendarKind::JapaneseExtended,
941            b"persian" => AnyCalendarKind::Persian,
942            b"roc" => AnyCalendarKind::Roc,
943            _ => {
944                // Log a warning when a calendar value is passed in but doesn't match any calendars
945                DataError::custom("bcp47_bytes did not match any calendars").with_debug_context(x);
946                return None;
947            }
948        })
949    }
950    /// Construct from a BCP-47 [`Value`]
951    ///
952    /// Returns `None` if the calendar is unknown. If you prefer an error, use
953    /// [`CalendarError::unknown_any_calendar_kind`].
954    pub fn get_for_bcp47_value(x: &Value) -> Option<Self> {
955        match *x.as_tinystr_slice() {
956            [first] if first == "buddhist" => Some(AnyCalendarKind::Buddhist),
957            [first] if first == "chinese" => Some(AnyCalendarKind::Chinese),
958            [first] if first == "coptic" => Some(AnyCalendarKind::Coptic),
959            [first] if first == "dangi" => Some(AnyCalendarKind::Dangi),
960            [first] if first == "ethioaa" => Some(AnyCalendarKind::EthiopianAmeteAlem),
961            [first] if first == "ethiopic" => Some(AnyCalendarKind::Ethiopian),
962            [first] if first == "gregory" => Some(AnyCalendarKind::Gregorian),
963            [first] if first == "hebrew" => Some(AnyCalendarKind::Hebrew),
964            [first] if first == "indian" => Some(AnyCalendarKind::Indian),
965            [first] if first == "islamic" => Some(AnyCalendarKind::IslamicObservational),
966            [first] if first == "islamicc" => Some(AnyCalendarKind::IslamicCivil),
967            [first, second] if first == "islamic" && second == "civil" => {
968                Some(AnyCalendarKind::IslamicCivil)
969            }
970            [first, second] if first == "islamic" && second == "tbla" => {
971                Some(AnyCalendarKind::IslamicTabular)
972            }
973            [first, second] if first == "islamic" && second == "umalqura" => {
974                Some(AnyCalendarKind::IslamicUmmAlQura)
975            }
976            [first] if first == "iso" => Some(AnyCalendarKind::Iso),
977            [first] if first == "japanese" => Some(AnyCalendarKind::Japanese),
978            [first] if first == "japanext" => Some(AnyCalendarKind::JapaneseExtended),
979            [first] if first == "persian" => Some(AnyCalendarKind::Persian),
980            [first] if first == "roc" => Some(AnyCalendarKind::Roc),
981            _ => {
982                // Log a warning when a calendar value is passed in but doesn't match any calendars
983                DataError::custom("bcp47_value did not match any calendars")
984                    .with_display_context(x);
985                None
986            }
987        }
988    }
989
990    /// Convert to a BCP-47 string
991    pub fn as_bcp47_string(self) -> &'static str {
992        match self {
993            AnyCalendarKind::Buddhist => "buddhist",
994            AnyCalendarKind::Chinese => "chinese",
995            AnyCalendarKind::Coptic => "coptic",
996            AnyCalendarKind::Dangi => "dangi",
997            AnyCalendarKind::Ethiopian => "ethiopic",
998            AnyCalendarKind::EthiopianAmeteAlem => "ethioaa",
999            AnyCalendarKind::Gregorian => "gregory",
1000            AnyCalendarKind::Hebrew => "hebrew",
1001            AnyCalendarKind::Indian => "indian",
1002            AnyCalendarKind::IslamicCivil => "islamic-civil",
1003            AnyCalendarKind::IslamicObservational => "islamic",
1004            AnyCalendarKind::IslamicTabular => "islamic-tbla",
1005            AnyCalendarKind::IslamicUmmAlQura => "islamic-umalqura",
1006            AnyCalendarKind::Iso => "iso",
1007            AnyCalendarKind::Japanese => "japanese",
1008            AnyCalendarKind::JapaneseExtended => "japanext",
1009            AnyCalendarKind::Persian => "persian",
1010            AnyCalendarKind::Roc => "roc",
1011        }
1012    }
1013
1014    /// Convert to a BCP-47 `Value`
1015    #[allow(clippy::unwrap_used)] // these are known-good BCP47 unicode extension values
1016    pub fn as_bcp47_value(self) -> Value {
1017        match self {
1018            AnyCalendarKind::Buddhist => value!("buddhist"),
1019            AnyCalendarKind::Chinese => value!("chinese"),
1020            AnyCalendarKind::Coptic => value!("coptic"),
1021            AnyCalendarKind::Dangi => value!("dangi"),
1022            AnyCalendarKind::Ethiopian => value!("ethiopic"),
1023            AnyCalendarKind::EthiopianAmeteAlem => value!("ethioaa"),
1024            AnyCalendarKind::Gregorian => value!("gregory"),
1025            AnyCalendarKind::Hebrew => value!("hebrew"),
1026            AnyCalendarKind::Indian => value!("indian"),
1027            AnyCalendarKind::IslamicCivil => Value::try_from_bytes(b"islamic-civil").unwrap(),
1028            AnyCalendarKind::IslamicObservational => value!("islamic"),
1029            AnyCalendarKind::IslamicTabular => Value::try_from_bytes(b"islamic-tbla").unwrap(),
1030            AnyCalendarKind::IslamicUmmAlQura => {
1031                Value::try_from_bytes(b"islamic-umalqura").unwrap()
1032            }
1033            AnyCalendarKind::Iso => value!("iso"),
1034            AnyCalendarKind::Japanese => value!("japanese"),
1035            AnyCalendarKind::JapaneseExtended => value!("japanext"),
1036            AnyCalendarKind::Persian => value!("persian"),
1037            AnyCalendarKind::Roc => value!("roc"),
1038        }
1039    }
1040
1041    fn debug_name(self) -> &'static str {
1042        match self {
1043            AnyCalendarKind::Buddhist => Buddhist.debug_name(),
1044            AnyCalendarKind::Chinese => Chinese::DEBUG_NAME,
1045            AnyCalendarKind::Coptic => Coptic.debug_name(),
1046            AnyCalendarKind::Dangi => Dangi::DEBUG_NAME,
1047            AnyCalendarKind::Ethiopian => Ethiopian(false).debug_name(),
1048            AnyCalendarKind::EthiopianAmeteAlem => Ethiopian(true).debug_name(),
1049            AnyCalendarKind::Gregorian => Gregorian.debug_name(),
1050            AnyCalendarKind::Hebrew => Hebrew.debug_name(),
1051            AnyCalendarKind::Indian => Indian.debug_name(),
1052            AnyCalendarKind::IslamicCivil => IslamicCivil.debug_name(),
1053            AnyCalendarKind::IslamicObservational => IslamicObservational::DEBUG_NAME,
1054            AnyCalendarKind::IslamicTabular => IslamicTabular.debug_name(),
1055            AnyCalendarKind::IslamicUmmAlQura => IslamicUmmAlQura::DEBUG_NAME,
1056            AnyCalendarKind::Iso => Iso.debug_name(),
1057            AnyCalendarKind::Japanese => Japanese::DEBUG_NAME,
1058            AnyCalendarKind::JapaneseExtended => JapaneseExtended::DEBUG_NAME,
1059            AnyCalendarKind::Persian => Persian.debug_name(),
1060            AnyCalendarKind::Roc => Roc.debug_name(),
1061        }
1062    }
1063
1064    /// Extract the calendar component from a [`Locale`]
1065    ///
1066    /// Returns `None` if the calendar is not specified or unknown. If you prefer an error, use
1067    /// [`CalendarError::unknown_any_calendar_kind`].
1068    pub fn get_for_locale(l: &Locale) -> Option<Self> {
1069        l.extensions
1070            .unicode
1071            .keywords
1072            .get(&key!("ca"))
1073            .and_then(Self::get_for_bcp47_value)
1074    }
1075
1076    /// Extract the calendar component from a [`DataLocale`]
1077    ///
1078    /// Returns `None` if the calendar is not specified or unknown. If you prefer an error, use
1079    /// [`CalendarError::unknown_any_calendar_kind`].
1080    fn get_for_data_locale(l: &DataLocale) -> Option<Self> {
1081        l.get_unicode_ext(&key!("ca"))
1082            .and_then(|v| Self::get_for_bcp47_value(&v))
1083    }
1084
1085    // Do not make public, this will eventually need fallback
1086    // data from the provider
1087    fn from_data_locale_with_fallback(l: &DataLocale) -> Self {
1088        if let Some(kind) = Self::get_for_data_locale(l) {
1089            kind
1090        } else {
1091            let lang = l.language();
1092            if lang == language!("th") {
1093                Self::Buddhist
1094            // Other known fallback routes for currently-unsupported calendars
1095            // } else if lang == language!("sa") {
1096            //     Self::IslamicUmalqura
1097            // } else if lang == language!("af") || lang == language!("ir") {
1098            //     Self::Persian
1099            } else {
1100                Self::Gregorian
1101            }
1102        }
1103    }
1104}
1105
1106impl fmt::Display for AnyCalendarKind {
1107    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1108        fmt::Debug::fmt(self, f)
1109    }
1110}
1111
1112impl<C: IntoAnyCalendar> From<C> for AnyCalendar {
1113    fn from(c: C) -> AnyCalendar {
1114        c.to_any()
1115    }
1116}
1117
1118/// Trait for calendars that may be converted to [`AnyCalendar`]
1119pub trait IntoAnyCalendar: Calendar + Sized {
1120    /// Convert this calendar into an [`AnyCalendar`], moving it
1121    ///
1122    /// You should not need to call this method directly
1123    fn to_any(self) -> AnyCalendar;
1124
1125    /// Convert this calendar into an [`AnyCalendar`], cloning it
1126    ///
1127    /// You should not need to call this method directly
1128    fn to_any_cloned(&self) -> AnyCalendar;
1129    /// Convert a date for this calendar into an [`AnyDateInner`]
1130    ///
1131    /// You should not need to call this method directly
1132    fn date_to_any(&self, d: &Self::DateInner) -> AnyDateInner;
1133}
1134
1135impl IntoAnyCalendar for Buddhist {
1136    fn to_any(self) -> AnyCalendar {
1137        AnyCalendar::Buddhist(Buddhist)
1138    }
1139    fn to_any_cloned(&self) -> AnyCalendar {
1140        AnyCalendar::Buddhist(Buddhist)
1141    }
1142    fn date_to_any(&self, d: &Self::DateInner) -> AnyDateInner {
1143        AnyDateInner::Buddhist(*d)
1144    }
1145}
1146
1147impl IntoAnyCalendar for Chinese {
1148    fn to_any(self) -> AnyCalendar {
1149        AnyCalendar::Chinese(self)
1150    }
1151    fn to_any_cloned(&self) -> AnyCalendar {
1152        AnyCalendar::Chinese(self.clone())
1153    }
1154    fn date_to_any(&self, d: &Self::DateInner) -> AnyDateInner {
1155        AnyDateInner::Chinese(*d)
1156    }
1157}
1158
1159impl IntoAnyCalendar for Coptic {
1160    fn to_any(self) -> AnyCalendar {
1161        AnyCalendar::Coptic(Coptic)
1162    }
1163    fn to_any_cloned(&self) -> AnyCalendar {
1164        AnyCalendar::Coptic(Coptic)
1165    }
1166    fn date_to_any(&self, d: &Self::DateInner) -> AnyDateInner {
1167        AnyDateInner::Coptic(*d)
1168    }
1169}
1170
1171impl IntoAnyCalendar for Dangi {
1172    fn to_any(self) -> AnyCalendar {
1173        AnyCalendar::Dangi(self)
1174    }
1175    fn to_any_cloned(&self) -> AnyCalendar {
1176        AnyCalendar::Dangi(self.clone())
1177    }
1178    fn date_to_any(&self, d: &Self::DateInner) -> AnyDateInner {
1179        AnyDateInner::Dangi(*d)
1180    }
1181}
1182
1183impl IntoAnyCalendar for Ethiopian {
1184    // Amete Mihret calendars are the default
1185    fn to_any(self) -> AnyCalendar {
1186        AnyCalendar::Ethiopian(self)
1187    }
1188    fn to_any_cloned(&self) -> AnyCalendar {
1189        AnyCalendar::Ethiopian(*self)
1190    }
1191    fn date_to_any(&self, d: &Self::DateInner) -> AnyDateInner {
1192        AnyDateInner::Ethiopian(*d)
1193    }
1194}
1195
1196impl IntoAnyCalendar for Gregorian {
1197    fn to_any(self) -> AnyCalendar {
1198        AnyCalendar::Gregorian(Gregorian)
1199    }
1200    fn to_any_cloned(&self) -> AnyCalendar {
1201        AnyCalendar::Gregorian(Gregorian)
1202    }
1203    fn date_to_any(&self, d: &Self::DateInner) -> AnyDateInner {
1204        AnyDateInner::Gregorian(*d)
1205    }
1206}
1207
1208impl IntoAnyCalendar for Hebrew {
1209    fn to_any(self) -> AnyCalendar {
1210        AnyCalendar::Hebrew(Hebrew)
1211    }
1212    fn to_any_cloned(&self) -> AnyCalendar {
1213        AnyCalendar::Hebrew(Hebrew)
1214    }
1215    fn date_to_any(&self, d: &Self::DateInner) -> AnyDateInner {
1216        AnyDateInner::Hebrew(*d)
1217    }
1218}
1219
1220impl IntoAnyCalendar for Indian {
1221    fn to_any(self) -> AnyCalendar {
1222        AnyCalendar::Indian(Indian)
1223    }
1224    fn to_any_cloned(&self) -> AnyCalendar {
1225        AnyCalendar::Indian(Indian)
1226    }
1227    fn date_to_any(&self, d: &Self::DateInner) -> AnyDateInner {
1228        AnyDateInner::Indian(*d)
1229    }
1230}
1231
1232impl IntoAnyCalendar for IslamicCivil {
1233    fn to_any(self) -> AnyCalendar {
1234        AnyCalendar::IslamicCivil(self)
1235    }
1236    fn to_any_cloned(&self) -> AnyCalendar {
1237        AnyCalendar::IslamicCivil(*self)
1238    }
1239    fn date_to_any(&self, d: &Self::DateInner) -> AnyDateInner {
1240        AnyDateInner::IslamicCivil(*d)
1241    }
1242}
1243
1244impl IntoAnyCalendar for IslamicObservational {
1245    fn to_any(self) -> AnyCalendar {
1246        AnyCalendar::IslamicObservational(self)
1247    }
1248    fn to_any_cloned(&self) -> AnyCalendar {
1249        AnyCalendar::IslamicObservational(self.clone())
1250    }
1251    fn date_to_any(&self, d: &Self::DateInner) -> AnyDateInner {
1252        AnyDateInner::IslamicObservational(*d)
1253    }
1254}
1255
1256impl IntoAnyCalendar for IslamicTabular {
1257    fn to_any(self) -> AnyCalendar {
1258        AnyCalendar::IslamicTabular(self)
1259    }
1260    fn to_any_cloned(&self) -> AnyCalendar {
1261        AnyCalendar::IslamicTabular(*self)
1262    }
1263    fn date_to_any(&self, d: &Self::DateInner) -> AnyDateInner {
1264        AnyDateInner::IslamicTabular(*d)
1265    }
1266}
1267
1268impl IntoAnyCalendar for IslamicUmmAlQura {
1269    fn to_any(self) -> AnyCalendar {
1270        AnyCalendar::IslamicUmmAlQura(self)
1271    }
1272    fn to_any_cloned(&self) -> AnyCalendar {
1273        AnyCalendar::IslamicUmmAlQura(self.clone())
1274    }
1275    fn date_to_any(&self, d: &Self::DateInner) -> AnyDateInner {
1276        AnyDateInner::IslamicUmmAlQura(*d)
1277    }
1278}
1279
1280impl IntoAnyCalendar for Iso {
1281    fn to_any(self) -> AnyCalendar {
1282        AnyCalendar::Iso(Iso)
1283    }
1284    fn to_any_cloned(&self) -> AnyCalendar {
1285        AnyCalendar::Iso(Iso)
1286    }
1287    fn date_to_any(&self, d: &Self::DateInner) -> AnyDateInner {
1288        AnyDateInner::Iso(*d)
1289    }
1290}
1291
1292impl IntoAnyCalendar for Japanese {
1293    fn to_any(self) -> AnyCalendar {
1294        AnyCalendar::Japanese(self)
1295    }
1296    fn to_any_cloned(&self) -> AnyCalendar {
1297        AnyCalendar::Japanese(self.clone())
1298    }
1299    fn date_to_any(&self, d: &Self::DateInner) -> AnyDateInner {
1300        AnyDateInner::Japanese(*d)
1301    }
1302}
1303
1304impl IntoAnyCalendar for JapaneseExtended {
1305    fn to_any(self) -> AnyCalendar {
1306        AnyCalendar::JapaneseExtended(self)
1307    }
1308    fn to_any_cloned(&self) -> AnyCalendar {
1309        AnyCalendar::JapaneseExtended(self.clone())
1310    }
1311    fn date_to_any(&self, d: &Self::DateInner) -> AnyDateInner {
1312        AnyDateInner::JapaneseExtended(*d)
1313    }
1314}
1315
1316impl IntoAnyCalendar for Persian {
1317    fn to_any(self) -> AnyCalendar {
1318        AnyCalendar::Persian(Persian)
1319    }
1320    fn to_any_cloned(&self) -> AnyCalendar {
1321        AnyCalendar::Persian(Persian)
1322    }
1323    fn date_to_any(&self, d: &Self::DateInner) -> AnyDateInner {
1324        AnyDateInner::Persian(*d)
1325    }
1326}
1327
1328impl IntoAnyCalendar for Roc {
1329    fn to_any(self) -> AnyCalendar {
1330        AnyCalendar::Roc(Roc)
1331    }
1332    fn to_any_cloned(&self) -> AnyCalendar {
1333        AnyCalendar::Roc(Roc)
1334    }
1335    fn date_to_any(&self, d: &Self::DateInner) -> AnyDateInner {
1336        AnyDateInner::Roc(*d)
1337    }
1338}
1339
1340#[cfg(test)]
1341mod tests {
1342    use super::*;
1343    use crate::Ref;
1344    use core::convert::TryInto;
1345
1346    fn single_test_roundtrip(
1347        calendar: Ref<AnyCalendar>,
1348        era: &str,
1349        year: i32,
1350        month_code: &str,
1351        day: u8,
1352    ) {
1353        let era = types::Era(era.parse().expect("era must parse"));
1354        let month = types::MonthCode(month_code.parse().expect("month code must parse"));
1355
1356        let date = Date::try_new_from_codes(era, year, month, day, calendar).unwrap_or_else(|e| {
1357            panic!(
1358                "Failed to construct date for {} with {:?}, {}, {}, {}: {}",
1359                calendar.debug_name(),
1360                era,
1361                year,
1362                month,
1363                day,
1364                e,
1365            )
1366        });
1367
1368        let roundtrip_year = date.year();
1369        let roundtrip_era = roundtrip_year.era;
1370        let roundtrip_year = roundtrip_year.number;
1371        let roundtrip_month = date.month().code;
1372        let roundtrip_day = date.day_of_month().0.try_into().expect("Must fit in u8");
1373
1374        assert_eq!(
1375            (era, year, month, day),
1376            (
1377                roundtrip_era,
1378                roundtrip_year,
1379                roundtrip_month,
1380                roundtrip_day
1381            ),
1382            "Failed to roundtrip for calendar {}",
1383            calendar.debug_name()
1384        );
1385
1386        let iso = date.to_iso();
1387        let reconstructed = Date::new_from_iso(iso, calendar);
1388        assert_eq!(
1389            date, reconstructed,
1390            "Failed to roundtrip via iso with {era:?}, {year}, {month}, {day}"
1391        )
1392    }
1393
1394    fn single_test_error(
1395        calendar: Ref<AnyCalendar>,
1396        era: &str,
1397        year: i32,
1398        month_code: &str,
1399        day: u8,
1400        error: CalendarError,
1401    ) {
1402        let era = types::Era(era.parse().expect("era must parse"));
1403        let month = types::MonthCode(month_code.parse().expect("month code must parse"));
1404
1405        let date = Date::try_new_from_codes(era, year, month, day, calendar);
1406        assert_eq!(
1407            date,
1408            Err(error),
1409            "Construction with {era:?}, {year}, {month}, {day} did not return {error:?}"
1410        )
1411    }
1412
1413    #[test]
1414    fn test_any_construction() {
1415        let buddhist = AnyCalendar::new(AnyCalendarKind::Buddhist);
1416        let chinese = AnyCalendar::new(AnyCalendarKind::Chinese);
1417        let coptic = AnyCalendar::new(AnyCalendarKind::Coptic);
1418        let dangi = AnyCalendar::new(AnyCalendarKind::Dangi);
1419        let ethioaa = AnyCalendar::new(AnyCalendarKind::EthiopianAmeteAlem);
1420        let ethiopian = AnyCalendar::new(AnyCalendarKind::Ethiopian);
1421        let gregorian = AnyCalendar::new(AnyCalendarKind::Gregorian);
1422        let hebrew = AnyCalendar::new(AnyCalendarKind::Hebrew);
1423        let indian = AnyCalendar::new(AnyCalendarKind::Indian);
1424        let islamic_civil: AnyCalendar = AnyCalendar::new(AnyCalendarKind::IslamicCivil);
1425        let islamic_observational: AnyCalendar =
1426            AnyCalendar::new(AnyCalendarKind::IslamicObservational);
1427        let islamic_tabular: AnyCalendar = AnyCalendar::new(AnyCalendarKind::IslamicTabular);
1428        let islamic_umm_al_qura: AnyCalendar = AnyCalendar::new(AnyCalendarKind::IslamicUmmAlQura);
1429        let japanese = AnyCalendar::new(AnyCalendarKind::Japanese);
1430        let japanext = AnyCalendar::new(AnyCalendarKind::JapaneseExtended);
1431        let persian = AnyCalendar::new(AnyCalendarKind::Persian);
1432        let roc = AnyCalendar::new(AnyCalendarKind::Roc);
1433        let buddhist = Ref(&buddhist);
1434        let chinese = Ref(&chinese);
1435        let coptic = Ref(&coptic);
1436        let dangi = Ref(&dangi);
1437        let ethioaa = Ref(&ethioaa);
1438        let ethiopian = Ref(&ethiopian);
1439        let gregorian = Ref(&gregorian);
1440        let hebrew = Ref(&hebrew);
1441        let indian = Ref(&indian);
1442        let islamic_civil = Ref(&islamic_civil);
1443        let islamic_observational = Ref(&islamic_observational);
1444        let islamic_tabular = Ref(&islamic_tabular);
1445        let islamic_umm_al_qura = Ref(&islamic_umm_al_qura);
1446        let japanese = Ref(&japanese);
1447        let japanext = Ref(&japanext);
1448        let persian = Ref(&persian);
1449        let roc = Ref(&roc);
1450
1451        single_test_roundtrip(buddhist, "be", 100, "M03", 1);
1452        single_test_roundtrip(buddhist, "be", 2000, "M03", 1);
1453        single_test_roundtrip(buddhist, "be", -100, "M03", 1);
1454        single_test_error(
1455            buddhist,
1456            "be",
1457            100,
1458            "M13",
1459            1,
1460            CalendarError::UnknownMonthCode(
1461                "M13".parse().unwrap(),
1462                AnyCalendarKind::Buddhist.debug_name(),
1463            ),
1464        );
1465
1466        single_test_roundtrip(coptic, "ad", 100, "M03", 1);
1467        single_test_roundtrip(coptic, "ad", 2000, "M03", 1);
1468        // fails ISO roundtrip
1469        // single_test_roundtrip(coptic, "bd", 100, "M03", 1);
1470        single_test_roundtrip(coptic, "ad", 100, "M13", 1);
1471        single_test_error(
1472            coptic,
1473            "ad",
1474            100,
1475            "M14",
1476            1,
1477            CalendarError::UnknownMonthCode(
1478                "M14".parse().unwrap(),
1479                AnyCalendarKind::Coptic.debug_name(),
1480            ),
1481        );
1482        single_test_error(coptic, "ad", 0, "M03", 1, CalendarError::OutOfRange);
1483        single_test_error(coptic, "bd", 0, "M03", 1, CalendarError::OutOfRange);
1484
1485        single_test_roundtrip(ethiopian, "incar", 100, "M03", 1);
1486        single_test_roundtrip(ethiopian, "incar", 2000, "M03", 1);
1487        single_test_roundtrip(ethiopian, "incar", 2000, "M13", 1);
1488        // Fails ISO roundtrip due to https://github.com/unicode-org/icu4x/issues/2254
1489        // single_test_roundtrip(ethiopian, "pre-incar", 100, "M03", 1);
1490        single_test_error(ethiopian, "incar", 0, "M03", 1, CalendarError::OutOfRange);
1491        single_test_error(
1492            ethiopian,
1493            "pre-incar",
1494            0,
1495            "M03",
1496            1,
1497            CalendarError::OutOfRange,
1498        );
1499        single_test_error(
1500            ethiopian,
1501            "incar",
1502            100,
1503            "M14",
1504            1,
1505            CalendarError::UnknownMonthCode(
1506                "M14".parse().unwrap(),
1507                AnyCalendarKind::Ethiopian.debug_name(),
1508            ),
1509        );
1510
1511        single_test_roundtrip(ethioaa, "mundi", 7000, "M13", 1);
1512        single_test_roundtrip(ethioaa, "mundi", 7000, "M13", 1);
1513        // Fails ISO roundtrip due to https://github.com/unicode-org/icu4x/issues/2254
1514        // single_test_roundtrip(ethioaa, "mundi", 100, "M03", 1);
1515        single_test_error(
1516            ethiopian,
1517            "mundi",
1518            100,
1519            "M14",
1520            1,
1521            CalendarError::UnknownMonthCode(
1522                "M14".parse().unwrap(),
1523                AnyCalendarKind::Ethiopian.debug_name(),
1524            ),
1525        );
1526
1527        single_test_roundtrip(gregorian, "ce", 100, "M03", 1);
1528        single_test_roundtrip(gregorian, "ce", 2000, "M03", 1);
1529        single_test_roundtrip(gregorian, "bce", 100, "M03", 1);
1530        single_test_error(gregorian, "ce", 0, "M03", 1, CalendarError::OutOfRange);
1531        single_test_error(gregorian, "bce", 0, "M03", 1, CalendarError::OutOfRange);
1532
1533        single_test_error(
1534            gregorian,
1535            "bce",
1536            100,
1537            "M13",
1538            1,
1539            CalendarError::UnknownMonthCode(
1540                "M13".parse().unwrap(),
1541                AnyCalendarKind::Gregorian.debug_name(),
1542            ),
1543        );
1544
1545        single_test_roundtrip(indian, "saka", 100, "M03", 1);
1546        single_test_roundtrip(indian, "saka", 2000, "M12", 1);
1547        single_test_roundtrip(indian, "saka", -100, "M03", 1);
1548        single_test_roundtrip(indian, "saka", 0, "M03", 1);
1549        single_test_error(
1550            indian,
1551            "saka",
1552            100,
1553            "M13",
1554            1,
1555            CalendarError::UnknownMonthCode(
1556                "M13".parse().unwrap(),
1557                AnyCalendarKind::Indian.debug_name(),
1558            ),
1559        );
1560
1561        single_test_roundtrip(chinese, "chinese", 400, "M02", 5);
1562        single_test_roundtrip(chinese, "chinese", 4660, "M07", 29);
1563        single_test_roundtrip(chinese, "chinese", -100, "M11", 12);
1564        single_test_error(
1565            chinese,
1566            "chinese",
1567            4658,
1568            "M13",
1569            1,
1570            CalendarError::UnknownMonthCode(
1571                "M13".parse().unwrap(),
1572                AnyCalendarKind::Chinese.debug_name(),
1573            ),
1574        );
1575
1576        single_test_roundtrip(dangi, "dangi", 400, "M02", 5);
1577        single_test_roundtrip(dangi, "dangi", 4660, "M08", 29);
1578        single_test_roundtrip(dangi, "dangi", -1300, "M11", 12);
1579        single_test_error(
1580            dangi,
1581            "dangi",
1582            10393,
1583            "M00L",
1584            1,
1585            CalendarError::UnknownMonthCode(
1586                "M00L".parse().unwrap(),
1587                AnyCalendarKind::Dangi.debug_name(),
1588            ),
1589        );
1590
1591        single_test_roundtrip(japanese, "reiwa", 3, "M03", 1);
1592        single_test_roundtrip(japanese, "heisei", 6, "M12", 1);
1593        single_test_roundtrip(japanese, "meiji", 10, "M03", 1);
1594        single_test_roundtrip(japanese, "ce", 1000, "M03", 1);
1595        single_test_roundtrip(japanese, "bce", 10, "M03", 1);
1596        single_test_error(japanese, "ce", 0, "M03", 1, CalendarError::OutOfRange);
1597        single_test_error(japanese, "bce", 0, "M03", 1, CalendarError::OutOfRange);
1598
1599        single_test_error(
1600            japanese,
1601            "reiwa",
1602            2,
1603            "M13",
1604            1,
1605            CalendarError::UnknownMonthCode(
1606                "M13".parse().unwrap(),
1607                AnyCalendarKind::Japanese.debug_name(),
1608            ),
1609        );
1610
1611        single_test_roundtrip(japanext, "reiwa", 3, "M03", 1);
1612        single_test_roundtrip(japanext, "heisei", 6, "M12", 1);
1613        single_test_roundtrip(japanext, "meiji", 10, "M03", 1);
1614        single_test_roundtrip(japanext, "tenpyokampo-749", 1, "M04", 20);
1615        single_test_roundtrip(japanext, "ce", 100, "M03", 1);
1616        single_test_roundtrip(japanext, "bce", 10, "M03", 1);
1617        single_test_error(japanext, "ce", 0, "M03", 1, CalendarError::OutOfRange);
1618        single_test_error(japanext, "bce", 0, "M03", 1, CalendarError::OutOfRange);
1619
1620        single_test_error(
1621            japanext,
1622            "reiwa",
1623            2,
1624            "M13",
1625            1,
1626            CalendarError::UnknownMonthCode(
1627                "M13".parse().unwrap(),
1628                AnyCalendarKind::JapaneseExtended.debug_name(),
1629            ),
1630        );
1631
1632        single_test_roundtrip(persian, "ah", 477, "M03", 1);
1633        single_test_roundtrip(persian, "ah", 2083, "M07", 21);
1634        single_test_roundtrip(persian, "ah", 1600, "M12", 20);
1635        single_test_error(
1636            persian,
1637            "ah",
1638            100,
1639            "M9",
1640            1,
1641            CalendarError::UnknownMonthCode(
1642                "M9".parse().unwrap(),
1643                AnyCalendarKind::Persian.debug_name(),
1644            ),
1645        );
1646
1647        single_test_roundtrip(hebrew, "hebrew", 5773, "M03", 1);
1648        single_test_roundtrip(hebrew, "hebrew", 4993, "M07", 21);
1649        single_test_roundtrip(hebrew, "hebrew", 5012, "M12", 20);
1650        single_test_error(
1651            hebrew,
1652            "hebrew",
1653            100,
1654            "M9",
1655            1,
1656            CalendarError::UnknownMonthCode(
1657                "M9".parse().unwrap(),
1658                AnyCalendarKind::Hebrew.debug_name(),
1659            ),
1660        );
1661
1662        single_test_roundtrip(roc, "roc", 10, "M05", 3);
1663        single_test_roundtrip(roc, "roc-inverse", 15, "M01", 10);
1664        single_test_roundtrip(roc, "roc", 100, "M10", 30);
1665
1666        single_test_roundtrip(islamic_observational, "islamic", 477, "M03", 1);
1667        single_test_roundtrip(islamic_observational, "islamic", 2083, "M07", 21);
1668        single_test_roundtrip(islamic_observational, "islamic", 1600, "M12", 20);
1669        single_test_error(
1670            islamic_observational,
1671            "islamic",
1672            100,
1673            "M9",
1674            1,
1675            CalendarError::UnknownMonthCode(
1676                "M9".parse().unwrap(),
1677                AnyCalendarKind::IslamicObservational.debug_name(),
1678            ),
1679        );
1680
1681        single_test_roundtrip(islamic_civil, "islamic", 477, "M03", 1);
1682        single_test_roundtrip(islamic_civil, "islamic", 2083, "M07", 21);
1683        single_test_roundtrip(islamic_civil, "islamic", 1600, "M12", 20);
1684        single_test_error(
1685            islamic_civil,
1686            "islamic",
1687            100,
1688            "M9",
1689            1,
1690            CalendarError::UnknownMonthCode(
1691                "M9".parse().unwrap(),
1692                AnyCalendarKind::IslamicCivil.debug_name(),
1693            ),
1694        );
1695
1696        single_test_roundtrip(islamic_umm_al_qura, "islamic", 477, "M03", 1);
1697        single_test_roundtrip(islamic_umm_al_qura, "islamic", 2083, "M07", 21);
1698        single_test_roundtrip(islamic_umm_al_qura, "islamic", 1600, "M12", 20);
1699        single_test_error(
1700            islamic_umm_al_qura,
1701            "islamic",
1702            100,
1703            "M9",
1704            1,
1705            CalendarError::UnknownMonthCode(
1706                "M9".parse().unwrap(),
1707                AnyCalendarKind::IslamicUmmAlQura.debug_name(),
1708            ),
1709        );
1710
1711        single_test_roundtrip(islamic_tabular, "islamic", 477, "M03", 1);
1712        single_test_roundtrip(islamic_tabular, "islamic", 2083, "M07", 21);
1713        single_test_roundtrip(islamic_tabular, "islamic", 1600, "M12", 20);
1714        single_test_error(
1715            islamic_tabular,
1716            "islamic",
1717            100,
1718            "M9",
1719            1,
1720            CalendarError::UnknownMonthCode(
1721                "M9".parse().unwrap(),
1722                AnyCalendarKind::IslamicTabular.debug_name(),
1723            ),
1724        );
1725    }
1726}