icu_datetime/scaffold/
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//! Scaffolding traits and impls for calendars.
6
7use crate::provider::{neo::*, *};
8use crate::scaffold::UnstableSealed;
9use crate::{DateTimeFormatterPreferences, MismatchedCalendarError};
10use core::marker::PhantomData;
11use icu_calendar::cal::{self, *};
12use icu_calendar::{AnyCalendar, AnyCalendarKind, AsCalendar, Date, IntoAnyCalendar, Ref};
13use icu_provider::marker::NeverMarker;
14use icu_provider::prelude::*;
15use icu_time::{
16    zone::{models::TimeZoneModel, UtcOffset},
17    DateTime, Time, TimeZoneInfo, ZonedDateTime,
18};
19
20/// A calendar that can be found in CLDR.
21///
22/// New implementors of this trait will likely also wish to modify `get_era_code_map()`
23/// in the CLDR transformer to support any new era maps.
24///
25/// <div class="stab unstable">
26/// 🚫 This trait is sealed; it cannot be implemented by user code. If an API requests an item that implements this
27/// trait, please consider using a type from the implementors listed below.
28/// </div>
29pub trait CldrCalendar: UnstableSealed {
30    /// The data marker for loading year symbols for this calendar.
31    type YearNamesV1: DataMarker<DataStruct = YearNames<'static>>;
32
33    /// The data marker for loading month symbols for this calendar.
34    type MonthNamesV1: DataMarker<DataStruct = MonthNames<'static>>;
35
36    /// The data marker for loading skeleton patterns for this calendar.
37    type SkeletaV1: DataMarker<DataStruct = PackedPatterns<'static>>;
38}
39
40impl CldrCalendar for () {
41    type YearNamesV1 = NeverMarker<YearNames<'static>>;
42    type MonthNamesV1 = NeverMarker<MonthNames<'static>>;
43    type SkeletaV1 = NeverMarker<PackedPatterns<'static>>;
44}
45
46impl CldrCalendar for Buddhist {
47    type YearNamesV1 = DatetimeNamesYearBuddhistV1;
48    type MonthNamesV1 = DatetimeNamesMonthBuddhistV1;
49    type SkeletaV1 = DatetimePatternsDateBuddhistV1;
50}
51
52impl CldrCalendar for ChineseTraditional {
53    type YearNamesV1 = DatetimeNamesYearChineseV1;
54    type MonthNamesV1 = DatetimeNamesMonthChineseV1;
55    type SkeletaV1 = DatetimePatternsDateChineseV1;
56}
57
58impl CldrCalendar for Coptic {
59    type YearNamesV1 = DatetimeNamesYearCopticV1;
60    type MonthNamesV1 = DatetimeNamesMonthCopticV1;
61    type SkeletaV1 = DatetimePatternsDateCopticV1;
62}
63
64impl CldrCalendar for KoreanTraditional {
65    type YearNamesV1 = DatetimeNamesYearDangiV1;
66    type MonthNamesV1 = DatetimeNamesMonthDangiV1;
67    type SkeletaV1 = DatetimePatternsDateDangiV1;
68}
69
70impl CldrCalendar for Ethiopian {
71    type YearNamesV1 = DatetimeNamesYearEthiopianV1;
72    type MonthNamesV1 = DatetimeNamesMonthEthiopianV1;
73    type SkeletaV1 = DatetimePatternsDateEthiopianV1;
74}
75
76impl CldrCalendar for Gregorian {
77    type YearNamesV1 = DatetimeNamesYearGregorianV1;
78    type MonthNamesV1 = DatetimeNamesMonthGregorianV1;
79    type SkeletaV1 = DatetimePatternsDateGregorianV1;
80}
81
82impl CldrCalendar for Hebrew {
83    type YearNamesV1 = DatetimeNamesYearHebrewV1;
84    type MonthNamesV1 = DatetimeNamesMonthHebrewV1;
85    type SkeletaV1 = DatetimePatternsDateHebrewV1;
86}
87
88impl CldrCalendar for Indian {
89    type YearNamesV1 = DatetimeNamesYearIndianV1;
90    type MonthNamesV1 = DatetimeNamesMonthIndianV1;
91    type SkeletaV1 = DatetimePatternsDateIndianV1;
92}
93
94/// [`hijri::Rules`]-specific formatting options.
95///
96/// See [`CldrCalendar`].
97///
98/// The simplest implementation of this uses the same names
99/// as some provided [`hijri::Rules`]:
100///
101/// ```rust
102/// use icu::calendar::cal::hijri;
103/// use icu::datetime::scaffold::FormattableHijriRules;
104///
105/// #[derive(Clone, Debug)]
106/// struct MyRules;
107///
108/// impl icu::calendar::cal::scaffold::UnstableSealed for MyRules {}
109/// impl icu::datetime::scaffold::UnstableSealed for MyRules {}
110///
111/// impl hijri::Rules for MyRules {
112///     fn year_data(&self, _year: i32) -> hijri::HijriYearData {
113///         todo!()
114///     }
115/// }
116///
117/// impl FormattableHijriRules for MyRules {
118///     type YearNamesV1 =
119///         <hijri::UmmAlQura as FormattableHijriRules>::YearNamesV1;
120///     type MonthNamesV1 =
121///         <hijri::UmmAlQura as FormattableHijriRules>::MonthNamesV1;
122///     type SkeletaV1 = <hijri::UmmAlQura as FormattableHijriRules>::SkeletaV1;
123/// }
124/// ```
125// TODO: default associated types would be nice (https://github.com/rust-lang/rust/issues/29661)
126pub trait FormattableHijriRules: hijri::unstable_internal::Rules + UnstableSealed {
127    /// The data marker for loading year symbols for this calendar.
128    type YearNamesV1: DataMarker<DataStruct = YearNames<'static>>;
129
130    /// The data marker for loading month symbols for this calendar.
131    type MonthNamesV1: DataMarker<DataStruct = MonthNames<'static>>;
132
133    /// The data marker for loading skeleton patterns for this calendar.
134    type SkeletaV1: DataMarker<DataStruct = PackedPatterns<'static>>;
135}
136
137impl UnstableSealed for hijri::TabularAlgorithm {}
138impl FormattableHijriRules for hijri::TabularAlgorithm {
139    type YearNamesV1 = DatetimeNamesYearHijriV1;
140    type MonthNamesV1 = DatetimeNamesMonthHijriV1;
141    type SkeletaV1 = DatetimePatternsDateHijriV1;
142}
143
144impl UnstableSealed for hijri::UmmAlQura {}
145impl FormattableHijriRules for hijri::UmmAlQura {
146    type YearNamesV1 = DatetimeNamesYearHijriV1;
147    type MonthNamesV1 = DatetimeNamesMonthHijriV1;
148    type SkeletaV1 = DatetimePatternsDateHijriV1;
149}
150
151impl UnstableSealed for hijri::AstronomicalSimulation {}
152impl FormattableHijriRules for hijri::AstronomicalSimulation {
153    type YearNamesV1 = DatetimeNamesYearHijriV1;
154    type MonthNamesV1 = DatetimeNamesMonthHijriV1;
155    type SkeletaV1 = DatetimePatternsDateHijriV1;
156}
157
158impl<R: FormattableHijriRules> CldrCalendar for Hijri<R> {
159    type YearNamesV1 = R::YearNamesV1;
160    type MonthNamesV1 = R::MonthNamesV1;
161    type SkeletaV1 = R::SkeletaV1;
162}
163
164impl CldrCalendar for Japanese {
165    type YearNamesV1 = DatetimeNamesYearJapaneseV1;
166    type MonthNamesV1 = DatetimeNamesMonthJapaneseV1;
167    type SkeletaV1 = DatetimePatternsDateJapaneseV1;
168}
169
170impl CldrCalendar for JapaneseExtended {
171    type YearNamesV1 = DatetimeNamesYearJapanextV1;
172    type MonthNamesV1 = DatetimeNamesMonthJapanextV1;
173    type SkeletaV1 = DatetimePatternsDateJapanextV1;
174}
175
176impl CldrCalendar for Persian {
177    type YearNamesV1 = DatetimeNamesYearPersianV1;
178    type MonthNamesV1 = DatetimeNamesMonthPersianV1;
179    type SkeletaV1 = DatetimePatternsDatePersianV1;
180}
181
182impl CldrCalendar for Roc {
183    type YearNamesV1 = DatetimeNamesYearRocV1;
184    type MonthNamesV1 = DatetimeNamesMonthRocV1;
185    type SkeletaV1 = DatetimePatternsDateRocV1;
186}
187
188impl UnstableSealed for () {}
189impl UnstableSealed for Buddhist {}
190impl UnstableSealed for ChineseTraditional {}
191impl UnstableSealed for Coptic {}
192impl UnstableSealed for KoreanTraditional {}
193impl UnstableSealed for Ethiopian {}
194impl UnstableSealed for Gregorian {}
195impl UnstableSealed for Hebrew {}
196impl UnstableSealed for Indian {}
197impl<R: hijri::unstable_internal::Rules> UnstableSealed for Hijri<R> {}
198impl UnstableSealed for Japanese {}
199impl UnstableSealed for JapaneseExtended {}
200impl UnstableSealed for Persian {}
201impl UnstableSealed for Roc {}
202
203/// A collection of marker types associated with all formattable calendars.
204///
205/// This is used to group together the calendar-specific marker types that produce a common
206/// [`DynamicDataMarker`]. For example, this trait can be implemented for [`YearNamesV1`].
207///
208/// This trait serves as a building block for a cross-calendar [`BoundDataProvider`].
209///
210/// <div class="stab unstable">
211/// 🚧 This trait is considered unstable; it may change at any time, in breaking or non-breaking ways,
212/// including in SemVer minor releases. Do not implement this trait in userland unless you are prepared for things to occasionally break.
213/// </div>
214pub trait CalMarkers<M>: UnstableSealed
215where
216    M: DynamicDataMarker,
217{
218    /// The type for a [`Buddhist`] calendar
219    type Buddhist: DataMarker<DataStruct = M::DataStruct>;
220    /// The type for a [`Chinese`] calendar
221    type Chinese: DataMarker<DataStruct = M::DataStruct>;
222    /// The type for a [`Coptic`] calendar
223    type Coptic: DataMarker<DataStruct = M::DataStruct>;
224    /// The type for a [`Dangi`] calendar
225    type Dangi: DataMarker<DataStruct = M::DataStruct>;
226    /// The type for an [`Ethiopian`] calendar (either era style)
227    type Ethiopian: DataMarker<DataStruct = M::DataStruct>;
228    /// The type for a [`Gregorian`] calendar
229    type Gregorian: DataMarker<DataStruct = M::DataStruct>;
230    /// The type for a [`Hebrew`] calendar
231    type Hebrew: DataMarker<DataStruct = M::DataStruct>;
232    /// The type for a [`Indian`] calendar
233    type Indian: DataMarker<DataStruct = M::DataStruct>;
234    /// The type for Hirji calendars
235    type Hijri: DataMarker<DataStruct = M::DataStruct>;
236    /// The type for a [`Japanese`] calendar
237    type Japanese: DataMarker<DataStruct = M::DataStruct>;
238    /// The type for a [`Persian`] calendar
239    type Persian: DataMarker<DataStruct = M::DataStruct>;
240    /// The type for a [`Roc`] calendar
241    type Roc: DataMarker<DataStruct = M::DataStruct>;
242}
243
244/// Implementation of [`CalMarkers`] that includes data for all calendars.
245#[derive(Debug)]
246#[allow(clippy::exhaustive_enums)] // empty enum
247pub enum FullDataCalMarkers {}
248
249impl UnstableSealed for FullDataCalMarkers {}
250
251/// Implementation of [`CalMarkers`] that includes data for no calendars.
252#[derive(Debug)]
253#[allow(clippy::exhaustive_enums)] // empty enum
254pub enum NoDataCalMarkers {}
255
256impl UnstableSealed for NoDataCalMarkers {}
257
258impl<M> CalMarkers<M> for NoDataCalMarkers
259where
260    M: DynamicDataMarker,
261{
262    type Buddhist = NeverMarker<M::DataStruct>;
263    type Chinese = NeverMarker<M::DataStruct>;
264    type Coptic = NeverMarker<M::DataStruct>;
265    type Dangi = NeverMarker<M::DataStruct>;
266    type Ethiopian = NeverMarker<M::DataStruct>;
267    type Gregorian = NeverMarker<M::DataStruct>;
268    type Hebrew = NeverMarker<M::DataStruct>;
269    type Indian = NeverMarker<M::DataStruct>;
270    type Hijri = NeverMarker<M::DataStruct>;
271    type Japanese = NeverMarker<M::DataStruct>;
272    type Persian = NeverMarker<M::DataStruct>;
273    type Roc = NeverMarker<M::DataStruct>;
274}
275
276/// A calendar type that is supported by [`DateTimeFormatter`](crate::DateTimeFormatter).
277///
278/// [`FixedCalendarDateTimeFormatter`](crate::FixedCalendarDateTimeFormatter) might support additional calendars.
279pub trait IntoFormattableAnyCalendar: CldrCalendar + IntoAnyCalendar {}
280
281// keep in sync with FormattableAnyCalendarKind
282impl IntoFormattableAnyCalendar for Buddhist {}
283impl IntoFormattableAnyCalendar for ChineseTraditional {}
284impl IntoFormattableAnyCalendar for Coptic {}
285impl IntoFormattableAnyCalendar for KoreanTraditional {}
286impl IntoFormattableAnyCalendar for Ethiopian {}
287impl IntoFormattableAnyCalendar for Gregorian {}
288impl IntoFormattableAnyCalendar for Hebrew {}
289impl IntoFormattableAnyCalendar for Indian {}
290impl IntoFormattableAnyCalendar for Hijri<hijri::TabularAlgorithm> {}
291impl IntoFormattableAnyCalendar for Hijri<hijri::AstronomicalSimulation> {}
292impl IntoFormattableAnyCalendar for Hijri<hijri::UmmAlQura> {}
293// _NOT_ Hijri<S>
294impl IntoFormattableAnyCalendar for Japanese {}
295// _NOT_ JapaneseExtended
296impl IntoFormattableAnyCalendar for Persian {}
297impl IntoFormattableAnyCalendar for Roc {}
298
299// keep in sync with IntoFormattableAnyCalendar
300#[derive(Debug, Clone, Copy, PartialEq)]
301pub(crate) enum FormattableAnyCalendarKind {
302    Buddhist,
303    Chinese,
304    Coptic,
305    Dangi,
306    Ethiopian,
307    EthiopianAmeteAlem,
308    Gregorian,
309    Hebrew,
310    Indian,
311    HijriTabularTypeIIFriday,
312    // _NOT_ HijriSimulatedMecca
313    HijriTabularTypeIIThursday,
314    HijriUmmAlQura,
315    Japanese,
316    // _NOT_ JapaneseExtended
317    Persian,
318    Roc,
319}
320
321impl FormattableAnyCalendarKind {
322    pub(crate) fn try_from_any_calendar_kind(kind: AnyCalendarKind) -> Option<Self> {
323        use AnyCalendarKind::*;
324        let res = match kind {
325            Buddhist => Self::Buddhist,
326            Chinese => Self::Chinese,
327            Coptic => Self::Coptic,
328            Dangi => Self::Dangi,
329            Ethiopian => Self::Ethiopian,
330            EthiopianAmeteAlem => Self::EthiopianAmeteAlem,
331            Gregorian => Self::Gregorian,
332            Hebrew => Self::Hebrew,
333            Indian => Self::Indian,
334            HijriTabularTypeIIFriday => Self::HijriTabularTypeIIFriday,
335            HijriSimulatedMecca => return None,
336            HijriTabularTypeIIThursday => Self::HijriTabularTypeIIThursday,
337            HijriUmmAlQura => Self::HijriUmmAlQura,
338            Iso => return None,
339            Japanese => Self::Japanese,
340            JapaneseExtended => return None,
341            Persian => Self::Persian,
342            Roc => Self::Roc,
343            _ => {
344                debug_assert!(false, "cross-crate exhaustive match");
345                return None;
346            }
347        };
348        Some(res)
349    }
350
351    pub(crate) fn from_preferences(mut prefs: DateTimeFormatterPreferences) -> Self {
352        if let Some(res) = Self::try_from_any_calendar_kind(AnyCalendarKind::new((&prefs).into())) {
353            return res;
354        }
355
356        // Calendar not supported by DateTimeFormatter
357        // Currently this is CalendarAlgorithm::Iso8601, CalendarAlgorithm::Hijri(Rgsa)
358        // Let AnyCalendarKind constructor select an appropriate fallback
359        prefs.calendar_algorithm = None;
360        if let Some(res) = Self::try_from_any_calendar_kind(AnyCalendarKind::new((&prefs).into())) {
361            return res;
362        }
363
364        debug_assert!(false, "all locale-default calendars are supported");
365        // fall back to something non-Gregorian to make errors more obvious
366        FormattableAnyCalendarKind::Coptic
367    }
368}
369
370#[test]
371fn test_calendar_fallback() {
372    use icu_locale_core::locale;
373    assert_eq!(
374        FormattableAnyCalendarKind::from_preferences(locale!("en-TH-u-ca-iso8601").into()),
375        FormattableAnyCalendarKind::Buddhist
376    );
377    assert_eq!(
378        FormattableAnyCalendarKind::from_preferences(locale!("en-TH").into()),
379        FormattableAnyCalendarKind::Buddhist
380    );
381    assert_eq!(
382        FormattableAnyCalendarKind::from_preferences(locale!("en-SA-u-ca-islamic").into()),
383        FormattableAnyCalendarKind::HijriUmmAlQura
384    );
385    assert_eq!(
386        FormattableAnyCalendarKind::from_preferences(locale!("en-IL-u-ca-islamic").into()),
387        FormattableAnyCalendarKind::HijriTabularTypeIIFriday
388    );
389}
390
391/// A version of [`AnyCalendar`] for the calendars supported in the any-calendar formatter.
392#[derive(Debug, Clone)]
393pub(crate) struct FormattableAnyCalendar {
394    any_calendar: AnyCalendar,
395    kind: FormattableAnyCalendarKind,
396}
397
398impl FormattableAnyCalendar {
399    pub(crate) fn from_calendar(calendar: impl IntoFormattableAnyCalendar) -> Self {
400        let any_calendar = calendar.to_any();
401        let kind = any_calendar.kind();
402        let kind = FormattableAnyCalendarKind::try_from_any_calendar_kind(any_calendar.kind())
403            .unwrap_or_else(|| {
404                debug_assert!(false, "{kind:?} is not a FormattableAnyCalendarKind");
405                FormattableAnyCalendarKind::Coptic
406            });
407        Self { any_calendar, kind }
408    }
409
410    pub(crate) fn try_from_any_calendar(any_calendar: AnyCalendar) -> Option<Self> {
411        let kind = FormattableAnyCalendarKind::try_from_any_calendar_kind(any_calendar.kind())?;
412        Some(Self { any_calendar, kind })
413    }
414
415    pub(crate) fn kind(&self) -> FormattableAnyCalendarKind {
416        self.kind
417    }
418
419    #[cfg(feature = "compiled_data")]
420    pub(crate) fn try_new(kind: FormattableAnyCalendarKind) -> Result<Self, DataError> {
421        use FormattableAnyCalendarKind::*;
422        let any_calendar = match kind {
423            Buddhist => AnyCalendar::Buddhist(cal::Buddhist),
424            Chinese => AnyCalendar::Chinese(cal::ChineseTraditional::new()),
425            Coptic => AnyCalendar::Coptic(cal::Coptic),
426            Dangi => AnyCalendar::Dangi(cal::KoreanTraditional::new()),
427            Ethiopian => AnyCalendar::Ethiopian(cal::Ethiopian::new()),
428            EthiopianAmeteAlem => AnyCalendar::Ethiopian(cal::Ethiopian::new_with_era_style(
429                cal::EthiopianEraStyle::AmeteAlem,
430            )),
431            Gregorian => AnyCalendar::Gregorian(cal::Gregorian),
432            Hebrew => AnyCalendar::Hebrew(cal::Hebrew),
433            Indian => AnyCalendar::Indian(cal::Indian),
434            HijriTabularTypeIIFriday => AnyCalendar::HijriTabular(cal::Hijri::new_tabular(
435                hijri::TabularAlgorithmLeapYears::TypeII,
436                hijri::TabularAlgorithmEpoch::Friday,
437            )),
438            HijriTabularTypeIIThursday => AnyCalendar::HijriTabular(cal::Hijri::new_tabular(
439                hijri::TabularAlgorithmLeapYears::TypeII,
440                hijri::TabularAlgorithmEpoch::Thursday,
441            )),
442            HijriUmmAlQura => AnyCalendar::HijriUmmAlQura(cal::Hijri::new_umm_al_qura()),
443            Japanese => AnyCalendar::Japanese(cal::Japanese::new()),
444            Persian => AnyCalendar::Persian(cal::Persian),
445            Roc => AnyCalendar::Roc(cal::Roc),
446        };
447        Ok(Self { any_calendar, kind })
448    }
449
450    #[cfg(feature = "serde")]
451    pub(crate) fn try_new_with_buffer_provider<P>(
452        provider: &P,
453        kind: FormattableAnyCalendarKind,
454    ) -> Result<Self, DataError>
455    where
456        P: ?Sized + BufferProvider,
457    {
458        use FormattableAnyCalendarKind::*;
459        let any_calendar = match kind {
460            Buddhist => AnyCalendar::Buddhist(cal::Buddhist),
461            Chinese => AnyCalendar::Chinese(cal::ChineseTraditional::new()),
462            Coptic => AnyCalendar::Coptic(cal::Coptic),
463            Dangi => AnyCalendar::Dangi(cal::KoreanTraditional::new()),
464            Ethiopian => AnyCalendar::Ethiopian(cal::Ethiopian::new()),
465            EthiopianAmeteAlem => AnyCalendar::Ethiopian(cal::Ethiopian::new_with_era_style(
466                cal::EthiopianEraStyle::AmeteAlem,
467            )),
468            Gregorian => AnyCalendar::Gregorian(cal::Gregorian),
469            Hebrew => AnyCalendar::Hebrew(cal::Hebrew),
470            Indian => AnyCalendar::Indian(cal::Indian),
471            HijriTabularTypeIIFriday => AnyCalendar::HijriTabular(cal::Hijri::new_tabular(
472                hijri::TabularAlgorithmLeapYears::TypeII,
473                hijri::TabularAlgorithmEpoch::Friday,
474            )),
475            HijriTabularTypeIIThursday => AnyCalendar::HijriTabular(cal::Hijri::new_tabular(
476                hijri::TabularAlgorithmLeapYears::TypeII,
477                hijri::TabularAlgorithmEpoch::Thursday,
478            )),
479            HijriUmmAlQura => AnyCalendar::HijriUmmAlQura(cal::Hijri::new_umm_al_qura()),
480            Japanese => {
481                AnyCalendar::Japanese(cal::Japanese::try_new_with_buffer_provider(provider)?)
482            }
483            Persian => AnyCalendar::Persian(cal::Persian),
484            Roc => AnyCalendar::Roc(cal::Roc),
485        };
486        Ok(Self { any_calendar, kind })
487    }
488
489    pub(crate) fn try_new_unstable<P>(
490        provider: &P,
491        kind: FormattableAnyCalendarKind,
492    ) -> Result<Self, DataError>
493    where
494        P: ?Sized + DataProvider<icu_calendar::provider::CalendarJapaneseModernV1>,
495    {
496        use FormattableAnyCalendarKind::*;
497        let any_calendar = match kind {
498            Buddhist => AnyCalendar::Buddhist(cal::Buddhist),
499            Chinese => AnyCalendar::Chinese(cal::ChineseTraditional::new()),
500            Coptic => AnyCalendar::Coptic(cal::Coptic),
501            Dangi => AnyCalendar::Dangi(cal::KoreanTraditional::new()),
502            Ethiopian => AnyCalendar::Ethiopian(cal::Ethiopian::new()),
503            EthiopianAmeteAlem => AnyCalendar::Ethiopian(cal::Ethiopian::new_with_era_style(
504                cal::EthiopianEraStyle::AmeteAlem,
505            )),
506            Gregorian => AnyCalendar::Gregorian(cal::Gregorian),
507            Hebrew => AnyCalendar::Hebrew(cal::Hebrew),
508            Indian => AnyCalendar::Indian(cal::Indian),
509            HijriTabularTypeIIFriday => AnyCalendar::HijriTabular(cal::Hijri::new_tabular(
510                hijri::TabularAlgorithmLeapYears::TypeII,
511                hijri::TabularAlgorithmEpoch::Friday,
512            )),
513            HijriTabularTypeIIThursday => AnyCalendar::HijriTabular(cal::Hijri::new_tabular(
514                hijri::TabularAlgorithmLeapYears::TypeII,
515                hijri::TabularAlgorithmEpoch::Thursday,
516            )),
517            HijriUmmAlQura => AnyCalendar::HijriUmmAlQura(cal::Hijri::new_umm_al_qura()),
518            Japanese => AnyCalendar::Japanese(cal::Japanese::try_new_unstable(provider)?),
519            Persian => AnyCalendar::Persian(cal::Persian),
520            Roc => AnyCalendar::Roc(cal::Roc),
521        };
522        Ok(Self { any_calendar, kind })
523    }
524
525    pub(crate) fn into_untagged(self) -> UntaggedFormattableAnyCalendar {
526        UntaggedFormattableAnyCalendar {
527            any_calendar: self.any_calendar,
528        }
529    }
530}
531
532#[derive(Debug, Clone)]
533pub(crate) struct UntaggedFormattableAnyCalendar {
534    // Invariant: the kind must be representable as an FormattableAnyCalendarKind
535    any_calendar: AnyCalendar,
536}
537
538/// A version of [`FormattableAnyCalendar`] that is smaller on the stack.
539impl UntaggedFormattableAnyCalendar {
540    pub(crate) fn into_tagged(self) -> FormattableAnyCalendar {
541        let kind = FormattableAnyCalendarKind::try_from_any_calendar_kind(self.any_calendar.kind())
542            .unwrap_or_else(|| {
543                debug_assert!(false, "unreachable by invariant");
544                // fall back to something non-Gregorian to make errors more obvious
545                FormattableAnyCalendarKind::Coptic
546            });
547        FormattableAnyCalendar {
548            any_calendar: self.any_calendar,
549            kind,
550        }
551    }
552
553    pub(crate) fn any_calendar(&self) -> &AnyCalendar {
554        &self.any_calendar
555    }
556
557    pub(crate) fn take_any_calendar(self) -> AnyCalendar {
558        self.any_calendar
559    }
560}
561
562pub(crate) struct FormattableAnyCalendarNamesLoader<H, P> {
563    provider: P,
564    kind: FormattableAnyCalendarKind,
565    _helper: PhantomData<H>,
566}
567
568impl<H, P> FormattableAnyCalendarNamesLoader<H, P> {
569    pub(crate) fn new(provider: P, kind: FormattableAnyCalendarKind) -> Self {
570        Self {
571            provider,
572            kind,
573            _helper: PhantomData,
574        }
575    }
576}
577
578impl<M, H, P> BoundDataProvider<M> for FormattableAnyCalendarNamesLoader<H, P>
579where
580    M: DynamicDataMarker,
581    H: CalMarkers<M>,
582    P: Sized
583        + DataProvider<H::Buddhist>
584        + DataProvider<H::Chinese>
585        + DataProvider<H::Coptic>
586        + DataProvider<H::Dangi>
587        + DataProvider<H::Ethiopian>
588        + DataProvider<H::Gregorian>
589        + DataProvider<H::Hebrew>
590        + DataProvider<H::Indian>
591        + DataProvider<H::Hijri>
592        + DataProvider<H::Japanese>
593        + DataProvider<H::Persian>
594        + DataProvider<H::Roc>,
595{
596    fn load_bound(&self, req: DataRequest) -> Result<DataResponse<M>, DataError> {
597        use FormattableAnyCalendarKind::*;
598        let p = &self.provider;
599        match self.kind {
600            Buddhist => H::Buddhist::bind(p).load_bound(req),
601            Chinese => H::Chinese::bind(p).load_bound(req),
602            Coptic => H::Coptic::bind(p).load_bound(req),
603            Dangi => H::Dangi::bind(p).load_bound(req),
604            Ethiopian | EthiopianAmeteAlem => H::Ethiopian::bind(p).load_bound(req),
605            Gregorian => H::Gregorian::bind(p).load_bound(req),
606            Hebrew => H::Hebrew::bind(p).load_bound(req),
607            Indian => H::Indian::bind(p).load_bound(req),
608            HijriTabularTypeIIFriday | HijriTabularTypeIIThursday | HijriUmmAlQura => {
609                H::Hijri::bind(p).load_bound(req)
610            }
611            Japanese => H::Japanese::bind(p).load_bound(req),
612            Persian => H::Persian::bind(p).load_bound(req),
613            Roc => H::Roc::bind(p).load_bound(req),
614        }
615    }
616    fn bound_marker(&self) -> DataMarkerInfo {
617        use FormattableAnyCalendarKind::*;
618        match self.kind {
619            Buddhist => H::Buddhist::INFO,
620            Chinese => H::Chinese::INFO,
621            Coptic => H::Coptic::INFO,
622            Dangi => H::Dangi::INFO,
623            Ethiopian | EthiopianAmeteAlem => H::Ethiopian::INFO,
624            Gregorian => H::Gregorian::INFO,
625            Hebrew => H::Hebrew::INFO,
626            Indian => H::Indian::INFO,
627            HijriTabularTypeIIFriday | HijriTabularTypeIIThursday | HijriUmmAlQura => {
628                H::Hijri::INFO
629            }
630            Japanese => H::Japanese::INFO,
631            Persian => H::Persian::INFO,
632            Roc => H::Roc::INFO,
633        }
634    }
635}
636
637impl CalMarkers<YearNamesV1> for FullDataCalMarkers {
638    type Buddhist = <Buddhist as CldrCalendar>::YearNamesV1;
639    type Chinese = <ChineseTraditional as CldrCalendar>::YearNamesV1;
640    type Coptic = <Coptic as CldrCalendar>::YearNamesV1;
641    type Dangi = <KoreanTraditional as CldrCalendar>::YearNamesV1;
642    type Ethiopian = <Ethiopian as CldrCalendar>::YearNamesV1;
643    type Gregorian = <Gregorian as CldrCalendar>::YearNamesV1;
644    type Hebrew = <Hebrew as CldrCalendar>::YearNamesV1;
645    type Indian = <Indian as CldrCalendar>::YearNamesV1;
646    type Hijri = <Hijri<hijri::UmmAlQura> as CldrCalendar>::YearNamesV1;
647    type Japanese = <Japanese as CldrCalendar>::YearNamesV1;
648    type Persian = <Persian as CldrCalendar>::YearNamesV1;
649    type Roc = <Roc as CldrCalendar>::YearNamesV1;
650}
651
652impl CalMarkers<MonthNamesV1> for FullDataCalMarkers {
653    type Buddhist = <Buddhist as CldrCalendar>::MonthNamesV1;
654    type Chinese = <ChineseTraditional as CldrCalendar>::MonthNamesV1;
655    type Coptic = <Coptic as CldrCalendar>::MonthNamesV1;
656    type Dangi = <KoreanTraditional as CldrCalendar>::MonthNamesV1;
657    type Ethiopian = <Ethiopian as CldrCalendar>::MonthNamesV1;
658    type Gregorian = <Gregorian as CldrCalendar>::MonthNamesV1;
659    type Hebrew = <Hebrew as CldrCalendar>::MonthNamesV1;
660    type Indian = <Indian as CldrCalendar>::MonthNamesV1;
661    type Hijri = <Hijri<hijri::UmmAlQura> as CldrCalendar>::MonthNamesV1;
662    type Japanese = <Japanese as CldrCalendar>::MonthNamesV1;
663    type Persian = <Persian as CldrCalendar>::MonthNamesV1;
664    type Roc = <Roc as CldrCalendar>::MonthNamesV1;
665}
666
667impl CalMarkers<ErasedPackedPatterns> for FullDataCalMarkers {
668    type Buddhist = <Buddhist as CldrCalendar>::SkeletaV1;
669    type Chinese = <ChineseTraditional as CldrCalendar>::SkeletaV1;
670    type Coptic = <Coptic as CldrCalendar>::SkeletaV1;
671    type Dangi = <KoreanTraditional as CldrCalendar>::SkeletaV1;
672    type Ethiopian = <Ethiopian as CldrCalendar>::SkeletaV1;
673    type Gregorian = <Gregorian as CldrCalendar>::SkeletaV1;
674    type Hebrew = <Hebrew as CldrCalendar>::SkeletaV1;
675    type Indian = <Indian as CldrCalendar>::SkeletaV1;
676    type Hijri = <Hijri<hijri::UmmAlQura> as CldrCalendar>::SkeletaV1;
677    type Japanese = <Japanese as CldrCalendar>::SkeletaV1;
678    type Persian = <Persian as CldrCalendar>::SkeletaV1;
679    type Roc = <Roc as CldrCalendar>::SkeletaV1;
680}
681
682/// A type that can be converted into a specific calendar system.
683// This trait is implementable
684pub trait ConvertCalendar {
685    /// The converted type. This can be the same as the receiver type.
686    type Converted<'a>: Sized;
687    /// Converts `self` to the specified [`AnyCalendar`].
688    fn to_calendar<'a>(&self, calendar: &'a AnyCalendar) -> Self::Converted<'a>;
689}
690
691impl<C: IntoAnyCalendar, A: AsCalendar<Calendar = C>> ConvertCalendar for Date<A> {
692    type Converted<'a> = Date<Ref<'a, AnyCalendar>>;
693    #[inline]
694    fn to_calendar<'a>(&self, calendar: &'a AnyCalendar) -> Self::Converted<'a> {
695        self.to_calendar(Ref(calendar))
696    }
697}
698
699impl ConvertCalendar for Time {
700    type Converted<'a> = Time;
701    #[inline]
702    fn to_calendar<'a>(&self, _: &'a AnyCalendar) -> Self::Converted<'a> {
703        *self
704    }
705}
706
707impl<C: IntoAnyCalendar, A: AsCalendar<Calendar = C>> ConvertCalendar for DateTime<A> {
708    type Converted<'a> = DateTime<Ref<'a, AnyCalendar>>;
709    #[inline]
710    fn to_calendar<'a>(&self, calendar: &'a AnyCalendar) -> Self::Converted<'a> {
711        DateTime {
712            date: self.date.to_calendar(Ref(calendar)),
713            time: self.time,
714        }
715    }
716}
717
718impl<C: IntoAnyCalendar, A: AsCalendar<Calendar = C>, Z: Copy> ConvertCalendar
719    for ZonedDateTime<A, Z>
720{
721    type Converted<'a> = ZonedDateTime<Ref<'a, AnyCalendar>, Z>;
722    #[inline]
723    fn to_calendar<'a>(&self, calendar: &'a AnyCalendar) -> Self::Converted<'a> {
724        ZonedDateTime {
725            date: self.date.to_calendar(Ref(calendar)),
726            time: self.time,
727            zone: self.zone,
728        }
729    }
730}
731
732impl<O: TimeZoneModel> ConvertCalendar for TimeZoneInfo<O> {
733    type Converted<'a> = TimeZoneInfo<O>;
734    #[inline]
735    fn to_calendar<'a>(&self, _: &'a AnyCalendar) -> Self::Converted<'a> {
736        *self
737    }
738}
739
740/// An input that may be associated with a specific runtime calendar.
741// This trait is implementable
742pub trait InSameCalendar {
743    /// Checks whether this type is compatible with the given calendar.
744    ///
745    /// Types that are agnostic to calendar systems should return `Ok(())`.
746    fn check_any_calendar_kind(
747        &self,
748        any_calendar_kind: AnyCalendarKind,
749    ) -> Result<(), MismatchedCalendarError>;
750}
751
752impl<C: IntoAnyCalendar, A: AsCalendar<Calendar = C>> InSameCalendar for Date<A> {
753    #[inline]
754    fn check_any_calendar_kind(
755        &self,
756        any_calendar_kind: AnyCalendarKind,
757    ) -> Result<(), MismatchedCalendarError> {
758        if self.calendar().kind() == any_calendar_kind {
759            Ok(())
760        } else {
761            Err(MismatchedCalendarError {
762                this_kind: any_calendar_kind,
763                date_kind: Some(self.calendar().kind()),
764            })
765        }
766    }
767}
768
769impl InSameCalendar for Time {
770    #[inline]
771    fn check_any_calendar_kind(&self, _: AnyCalendarKind) -> Result<(), MismatchedCalendarError> {
772        Ok(())
773    }
774}
775
776impl<C: IntoAnyCalendar, A: AsCalendar<Calendar = C>> InSameCalendar for DateTime<A> {
777    #[inline]
778    fn check_any_calendar_kind(
779        &self,
780        any_calendar_kind: AnyCalendarKind,
781    ) -> Result<(), MismatchedCalendarError> {
782        self.date.check_any_calendar_kind(any_calendar_kind)
783    }
784}
785
786impl<C: IntoAnyCalendar, A: AsCalendar<Calendar = C>, Z> InSameCalendar for ZonedDateTime<A, Z> {
787    #[inline]
788    fn check_any_calendar_kind(
789        &self,
790        any_calendar_kind: AnyCalendarKind,
791    ) -> Result<(), MismatchedCalendarError> {
792        self.date.check_any_calendar_kind(any_calendar_kind)
793    }
794}
795
796impl InSameCalendar for UtcOffset {
797    #[inline]
798    fn check_any_calendar_kind(&self, _: AnyCalendarKind) -> Result<(), MismatchedCalendarError> {
799        Ok(())
800    }
801}
802
803impl<O: TimeZoneModel> InSameCalendar for TimeZoneInfo<O> {
804    #[inline]
805    fn check_any_calendar_kind(&self, _: AnyCalendarKind) -> Result<(), MismatchedCalendarError> {
806        Ok(())
807    }
808}
809
810/// An input associated with a fixed, static calendar.
811// This trait is implementable
812pub trait InFixedCalendar<C> {}
813
814impl<C: CldrCalendar, A: AsCalendar<Calendar = C>> InFixedCalendar<C> for Date<A> {}
815
816impl<C> InFixedCalendar<C> for Time {}
817
818impl<C: CldrCalendar, A: AsCalendar<Calendar = C>> InFixedCalendar<C> for DateTime<A> {}
819
820impl<C: CldrCalendar, A: AsCalendar<Calendar = C>, Z> InFixedCalendar<C> for ZonedDateTime<A, Z> {}
821
822impl<C> InFixedCalendar<C> for UtcOffset {}
823
824impl<C, O: TimeZoneModel> InFixedCalendar<C> for TimeZoneInfo<O> {}