icu_datetime/pattern/
names.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
5use super::{
6    DateTimePattern, DateTimePatternFormatter, GetNameForCyclicYearError, GetNameForDayPeriodError,
7    GetNameForEraError, GetNameForMonthError, GetNameForWeekdayError, MonthPlaceholderValue,
8    PatternLoadError, UnsupportedCalendarError,
9};
10use crate::error::ErrorField;
11use crate::fieldsets::enums::{CompositeDateTimeFieldSet, CompositeFieldSet};
12use crate::provider::fields::{self, FieldLength, FieldSymbol};
13use crate::provider::neo::{marker_attrs, *};
14use crate::provider::pattern::PatternItem;
15use crate::provider::time_zones::tz;
16use crate::size_test_macro::size_test;
17use crate::FixedCalendarDateTimeFormatter;
18use crate::{external_loaders::*, DateTimeFormatterPreferences};
19use crate::{scaffold::*, DateTimeFormatter, DateTimeFormatterLoadError};
20use core::fmt;
21use core::marker::PhantomData;
22use icu_calendar::types::EraYear;
23use icu_calendar::AnyCalendar;
24use icu_decimal::options::DecimalFormatterOptions;
25use icu_decimal::options::GroupingStrategy;
26use icu_decimal::provider::{DecimalDigitsV1, DecimalSymbolsV1};
27use icu_decimal::DecimalFormatter;
28use icu_provider::prelude::*;
29
30/// Choices for loading year names.
31#[derive(Debug, Copy, Clone, PartialEq, Eq)]
32#[non_exhaustive]
33pub enum YearNameLength {
34    /// An abbreviated calendar-dependent year or era name.
35    ///
36    /// Examples:
37    ///
38    /// - "AD"
39    /// - "甲子"
40    Abbreviated,
41    /// A wide calendar-dependent year or era name.
42    ///
43    /// Examples:
44    ///
45    /// - "Anno Domini"
46    /// - "甲子"
47    Wide,
48    /// A narrow calendar-dependent year or era name. Not necesarily unique.
49    ///
50    /// Examples:
51    ///
52    /// - "A"
53    /// - "甲子"
54    Narrow,
55}
56
57impl YearNameLength {
58    pub(crate) fn to_attributes(self) -> &'static DataMarkerAttributes {
59        use marker_attrs::Length;
60        let length = match self {
61            YearNameLength::Abbreviated => Length::Abbr,
62            YearNameLength::Wide => Length::Wide,
63            YearNameLength::Narrow => Length::Narrow,
64        };
65        marker_attrs::name_attr_for(marker_attrs::Context::Format, length)
66    }
67
68    pub(crate) fn from_field_length(field_length: FieldLength) -> Option<Self> {
69        // UTS 35 says that "G..GGG" and "U..UUU" are all Abbreviated
70        let field_length = field_length.numeric_to_abbr();
71        match field_length {
72            FieldLength::Three => Some(YearNameLength::Abbreviated),
73            FieldLength::Four => Some(YearNameLength::Wide),
74            FieldLength::Five => Some(YearNameLength::Narrow),
75            _ => None,
76        }
77    }
78
79    /// Returns an [`ErrorField`] sufficient for error reporting.
80    pub(crate) fn to_approximate_error_field(self) -> ErrorField {
81        let field_length = match self {
82            YearNameLength::Abbreviated => FieldLength::Three,
83            YearNameLength::Wide => FieldLength::Four,
84            YearNameLength::Narrow => FieldLength::Five,
85        };
86        ErrorField(fields::Field {
87            symbol: FieldSymbol::Era,
88            length: field_length,
89        })
90    }
91}
92
93/// Choices for loading month names.
94#[derive(Debug, Copy, Clone, PartialEq, Eq)]
95#[non_exhaustive]
96pub enum MonthNameLength {
97    /// An abbreviated calendar-dependent month name for formatting with other fields.
98    ///
99    /// Example: "Sep"
100    Abbreviated,
101    /// A wide calendar-dependent month name for formatting with other fields.
102    ///
103    /// Example: "September"
104    Wide,
105    /// A narrow calendar-dependent month name for formatting with other fields. Not necesarily unique.
106    ///
107    /// Example: "S"
108    Narrow,
109    /// An abbreviated calendar-dependent month name for stand-alone display.
110    ///
111    /// Example: "Sep"
112    StandaloneAbbreviated,
113    /// A wide calendar-dependent month name for stand-alone display.
114    ///
115    /// Example: "September"
116    StandaloneWide,
117    /// A narrow calendar-dependent month name for stand-alone display. Not necesarily unique.
118    ///
119    /// Example: "S"
120    StandaloneNarrow,
121}
122
123impl MonthNameLength {
124    pub(crate) fn to_attributes(self) -> &'static DataMarkerAttributes {
125        use marker_attrs::{Context, Length};
126        let (context, length) = match self {
127            MonthNameLength::Abbreviated => (Context::Format, Length::Abbr),
128            MonthNameLength::Wide => (Context::Format, Length::Wide),
129            MonthNameLength::Narrow => (Context::Format, Length::Narrow),
130            MonthNameLength::StandaloneAbbreviated => (Context::Standalone, Length::Abbr),
131            MonthNameLength::StandaloneWide => (Context::Standalone, Length::Wide),
132            MonthNameLength::StandaloneNarrow => (Context::Standalone, Length::Narrow),
133        };
134        marker_attrs::name_attr_for(context, length)
135    }
136
137    pub(crate) fn from_field(
138        field_symbol: fields::Month,
139        field_length: FieldLength,
140    ) -> Option<Self> {
141        use fields::Month;
142        match (field_symbol, field_length) {
143            (Month::Format, FieldLength::Three) => Some(MonthNameLength::Abbreviated),
144            (Month::Format, FieldLength::Four) => Some(MonthNameLength::Wide),
145            (Month::Format, FieldLength::Five) => Some(MonthNameLength::Narrow),
146            (Month::StandAlone, FieldLength::Three) => Some(MonthNameLength::StandaloneAbbreviated),
147            (Month::StandAlone, FieldLength::Four) => Some(MonthNameLength::StandaloneWide),
148            (Month::StandAlone, FieldLength::Five) => Some(MonthNameLength::StandaloneNarrow),
149            _ => None,
150        }
151    }
152
153    /// Returns an [`ErrorField`] sufficient for error reporting.
154    pub(crate) fn to_approximate_error_field(self) -> ErrorField {
155        use fields::Month;
156        let (field_symbol, field_length) = match self {
157            MonthNameLength::Abbreviated => (Month::Format, FieldLength::Three),
158            MonthNameLength::Wide => (Month::Format, FieldLength::Four),
159            MonthNameLength::Narrow => (Month::Format, FieldLength::Five),
160            MonthNameLength::StandaloneAbbreviated => (Month::StandAlone, FieldLength::Three),
161            MonthNameLength::StandaloneWide => (Month::StandAlone, FieldLength::Four),
162            MonthNameLength::StandaloneNarrow => (Month::StandAlone, FieldLength::Five),
163        };
164        ErrorField(fields::Field {
165            symbol: FieldSymbol::Month(field_symbol),
166            length: field_length,
167        })
168    }
169}
170
171/// Choices for loading weekday names.
172#[derive(Debug, Copy, Clone, PartialEq, Eq)]
173#[non_exhaustive]
174pub enum WeekdayNameLength {
175    /// An abbreviated weekday name for formatting with other fields.
176    ///
177    /// Example: "Tue"
178    Abbreviated,
179    /// A wide weekday name for formatting with other fields.
180    ///
181    /// Example: "Tuesday"
182    Wide,
183    /// A narrow weekday name for formatting with other fields. Not necesarily unique.
184    ///
185    /// Example: "T"
186    Narrow,
187    /// A short weekday name for formatting with other fields.
188    ///
189    /// Example: "Tu"
190    Short,
191    /// An abbreviated weekday name for stand-alone display.
192    ///
193    /// Example: "Tue"
194    StandaloneAbbreviated,
195    /// A wide weekday name for stand-alone display.
196    ///
197    /// Example: "Tuesday"
198    StandaloneWide,
199    /// A narrow weekday name for stand-alone display. Not necesarily unique.
200    ///
201    /// Example: "T"
202    StandaloneNarrow,
203    /// A short weekday name for stand-alone display.
204    ///
205    /// Example: "Tu"
206    StandaloneShort,
207}
208
209impl WeekdayNameLength {
210    pub(crate) fn to_attributes(self) -> &'static DataMarkerAttributes {
211        use marker_attrs::{Context, Length};
212        // UTS 35 says that "e" and "E" have the same non-numeric names
213        let (context, length) = match self {
214            WeekdayNameLength::Abbreviated => (Context::Format, Length::Abbr),
215            WeekdayNameLength::Wide => (Context::Format, Length::Wide),
216            WeekdayNameLength::Narrow => (Context::Format, Length::Narrow),
217            WeekdayNameLength::Short => (Context::Format, Length::Short),
218            WeekdayNameLength::StandaloneAbbreviated => (Context::Standalone, Length::Abbr),
219            WeekdayNameLength::StandaloneWide => (Context::Standalone, Length::Wide),
220            WeekdayNameLength::StandaloneNarrow => (Context::Standalone, Length::Narrow),
221            WeekdayNameLength::StandaloneShort => (Context::Standalone, Length::Short),
222        };
223        marker_attrs::name_attr_for(context, length)
224    }
225
226    pub(crate) fn from_field(
227        field_symbol: fields::Weekday,
228        field_length: FieldLength,
229    ) -> Option<Self> {
230        use fields::Weekday;
231        // UTS 35 says that "e" and "E" have the same non-numeric names
232        let field_symbol = field_symbol.to_format_symbol();
233        // UTS 35 says that "E..EEE" are all Abbreviated
234        // However, this doesn't apply to "e" and "c".
235        let field_length = if matches!(field_symbol, fields::Weekday::Format) {
236            field_length.numeric_to_abbr()
237        } else {
238            field_length
239        };
240        match (field_symbol, field_length) {
241            (Weekday::Format, FieldLength::Three) => Some(WeekdayNameLength::Abbreviated),
242            (Weekday::Format, FieldLength::Four) => Some(WeekdayNameLength::Wide),
243            (Weekday::Format, FieldLength::Five) => Some(WeekdayNameLength::Narrow),
244            (Weekday::Format, FieldLength::Six) => Some(WeekdayNameLength::Short),
245            (Weekday::StandAlone, FieldLength::Three) => {
246                Some(WeekdayNameLength::StandaloneAbbreviated)
247            }
248            (Weekday::StandAlone, FieldLength::Four) => Some(WeekdayNameLength::StandaloneWide),
249            (Weekday::StandAlone, FieldLength::Five) => Some(WeekdayNameLength::StandaloneNarrow),
250            (Weekday::StandAlone, FieldLength::Six) => Some(WeekdayNameLength::StandaloneShort),
251            _ => None,
252        }
253    }
254
255    /// Returns an [`ErrorField`] sufficient for error reporting.
256    pub(crate) fn to_approximate_error_field(self) -> ErrorField {
257        use fields::Weekday;
258        let (field_symbol, field_length) = match self {
259            WeekdayNameLength::Abbreviated => (Weekday::Format, FieldLength::Three),
260            WeekdayNameLength::Wide => (Weekday::Format, FieldLength::Four),
261            WeekdayNameLength::Narrow => (Weekday::Format, FieldLength::Five),
262            WeekdayNameLength::Short => (Weekday::Format, FieldLength::Six),
263            WeekdayNameLength::StandaloneAbbreviated => (Weekday::StandAlone, FieldLength::Three),
264            WeekdayNameLength::StandaloneWide => (Weekday::StandAlone, FieldLength::Four),
265            WeekdayNameLength::StandaloneNarrow => (Weekday::StandAlone, FieldLength::Five),
266            WeekdayNameLength::StandaloneShort => (Weekday::StandAlone, FieldLength::Six),
267        };
268        ErrorField(fields::Field {
269            symbol: FieldSymbol::Weekday(field_symbol),
270            length: field_length,
271        })
272    }
273}
274
275/// Choices for loading day period names.
276#[derive(Debug, Copy, Clone, PartialEq, Eq)]
277#[non_exhaustive]
278pub enum DayPeriodNameLength {
279    /// An abbreviated 12-hour day period name, including display names for 0h and 12h.
280    ///
281    /// Examples:
282    ///
283    /// - "AM"
284    /// - "mid."
285    Abbreviated,
286    /// A wide 12-hour day period name, including display names for 0h and 12h.
287    ///
288    /// The wide form may be the same as the abbreviated form if the "real" long form
289    /// (eg "ante meridiem") is not customarily used.
290    ///
291    /// Examples:
292    ///
293    /// - "AM"
294    /// - "mignight"
295    Wide,
296    /// An abbreviated 12-hour day period name, including display names for 0h and 12h.
297    ///
298    /// The narrow form must be unique, unlike some other fields.
299    ///
300    /// Examples:
301    ///
302    /// - "AM"
303    /// - "md"
304    Narrow,
305}
306
307impl DayPeriodNameLength {
308    pub(crate) fn to_attributes(self) -> &'static DataMarkerAttributes {
309        use marker_attrs::Length;
310        let length = match self {
311            DayPeriodNameLength::Abbreviated => Length::Abbr,
312            DayPeriodNameLength::Wide => Length::Wide,
313            DayPeriodNameLength::Narrow => Length::Narrow,
314        };
315        marker_attrs::name_attr_for(marker_attrs::Context::Format, length)
316    }
317
318    pub(crate) fn from_field(
319        field_symbol: fields::DayPeriod,
320        field_length: FieldLength,
321    ) -> Option<Self> {
322        use fields::DayPeriod;
323        // Names for 'a' and 'b' are stored in the same data marker
324        let field_symbol = match field_symbol {
325            DayPeriod::NoonMidnight => DayPeriod::AmPm,
326            other => other,
327        };
328        // UTS 35 says that "a..aaa" and "b..bbb" are all Abbreviated
329        let field_length = field_length.numeric_to_abbr();
330        match (field_symbol, field_length) {
331            (DayPeriod::AmPm, FieldLength::Three) => Some(DayPeriodNameLength::Abbreviated),
332            (DayPeriod::AmPm, FieldLength::Four) => Some(DayPeriodNameLength::Wide),
333            (DayPeriod::AmPm, FieldLength::Five) => Some(DayPeriodNameLength::Narrow),
334            _ => None,
335        }
336    }
337
338    /// Returns an [`ErrorField`] sufficient for error reporting.
339    pub(crate) fn to_approximate_error_field(self) -> ErrorField {
340        // Names for 'a' and 'b' are stored in the same data marker
341        let field_symbol = fields::DayPeriod::AmPm;
342        let field_length = match self {
343            DayPeriodNameLength::Abbreviated => FieldLength::Three,
344            DayPeriodNameLength::Wide => FieldLength::Four,
345            DayPeriodNameLength::Narrow => FieldLength::Five,
346        };
347        ErrorField(fields::Field {
348            symbol: FieldSymbol::DayPeriod(field_symbol),
349            length: field_length,
350        })
351    }
352}
353
354pub(crate) struct EmptyDataProvider;
355
356impl<M> DataProvider<M> for EmptyDataProvider
357where
358    M: DataMarker,
359{
360    fn load(&self, base_req: DataRequest) -> Result<DataResponse<M>, DataError> {
361        Err(DataErrorKind::MarkerNotFound.with_req(M::INFO, base_req))
362    }
363}
364
365size_test!(
366    FixedCalendarDateTimeNames<icu_calendar::Gregorian>,
367    typed_date_time_names_size,
368    328
369);
370
371/// A low-level type that formats datetime patterns with localized names.
372/// The calendar should be chosen at compile time.
373#[doc = typed_date_time_names_size!()]
374///
375/// Type parameters:
376///
377/// 1. The calendar chosen at compile time for additional type safety
378/// 2. A field set containing the fields that might be formatted
379///
380/// By default, the field set is set to [`CompositeDateTimeFieldSet`],
381/// meaning that dates and times, but not time zones, are supported. A smaller
382/// field set results in smaller stack size.
383///
384/// To support all fields including time zones, use [`CompositeFieldSet`].
385///
386/// [`CompositeFieldSet`]: crate::fieldsets::enums::CompositeFieldSet
387/// [`CompositeDateTimeFieldSet`]: crate::fieldsets::enums::CompositeDateTimeFieldSet
388///
389/// # Examples
390///
391/// ```
392/// use icu::calendar::Gregorian;
393/// use icu::datetime::input::Date;
394/// use icu::datetime::pattern::FixedCalendarDateTimeNames;
395/// use icu::datetime::pattern::DateTimePattern;
396/// use icu::datetime::pattern::MonthNameLength;
397/// use icu::datetime::pattern::WeekdayNameLength;
398/// use icu::datetime::pattern::DayPeriodNameLength;
399/// use icu::locale::locale;
400/// use icu::datetime::input::{DateTime, Time};
401/// use writeable::assert_try_writeable_eq;
402///
403/// // Create an instance that can format abbreviated month, weekday, and day period names:
404/// let mut names: FixedCalendarDateTimeNames<Gregorian> =
405///     FixedCalendarDateTimeNames::try_new(locale!("uk").into()).unwrap();
406/// names
407///     .include_month_names(MonthNameLength::Abbreviated)
408///     .unwrap()
409///     .include_weekday_names(WeekdayNameLength::Abbreviated)
410///     .unwrap()
411///     .include_day_period_names(DayPeriodNameLength::Abbreviated)
412///     .unwrap();
413///
414/// // Create a pattern from a pattern string (note: K is the hour with h11 hour cycle):
415/// let pattern_str = "E MMM d y -- K:mm a";
416/// let pattern: DateTimePattern = pattern_str.parse().unwrap();
417///
418/// // Test it:
419/// let datetime = DateTime { date: Date::try_new_gregorian(2023, 11, 20).unwrap(), time: Time::try_new(12, 35, 3, 0).unwrap() };
420/// assert_try_writeable_eq!(names.with_pattern_unchecked(&pattern).format(&datetime), "пн лист. 20 2023 -- 0:35 пп");
421/// ```
422///
423/// If the correct data is not loaded, an error will occur:
424///
425/// ```
426/// use icu::calendar::Gregorian;
427/// use icu::datetime::input::Date;
428/// use icu::datetime::pattern::FormattedDateTimePatternError;
429/// use icu::datetime::parts;
430/// use icu::datetime::pattern::FixedCalendarDateTimeNames;
431/// use icu::datetime::pattern::{DateTimePattern, PatternLoadError};
432/// use icu::datetime::fieldsets::enums::CompositeFieldSet;
433/// use icu::locale::locale;
434/// use icu::time::zone::IanaParser;
435/// use icu::datetime::input::{Time, TimeZoneInfo, ZonedDateTime};
436/// use icu_provider_adapters::empty::EmptyDataProvider;
437/// use writeable::{Part, assert_try_writeable_parts_eq};
438///
439/// // Unstable API used only for error construction below
440/// use icu::datetime::provider::fields::{Field, FieldLength, FieldSymbol, Weekday};
441///
442/// // Create an instance that can format all fields (CompositeFieldSet):
443/// let mut names: FixedCalendarDateTimeNames<Gregorian, CompositeFieldSet> =
444///     FixedCalendarDateTimeNames::try_new(locale!("en").into()).unwrap();
445///
446/// // Create a pattern from a pattern string:
447/// let pattern_str = "'It is:' E MMM d y G 'at' h:mm:ssSSS a zzzz";
448/// let pattern: DateTimePattern = pattern_str.parse().unwrap();
449///
450/// // The pattern string contains lots of symbols including "E", "MMM", and "a",
451/// // but we did not load any data!
452///
453/// let mut dtz = ZonedDateTime::try_strict_from_str("2023-11-20T11:35:03+00:00[Europe/London]", Gregorian, IanaParser::new()).unwrap();
454///
455/// // Missing data is filled in on a best-effort basis, and an error is signaled.
456/// assert_try_writeable_parts_eq!(
457///     names.with_pattern_unchecked(&pattern).format(&dtz),
458///     "It is: mon M11 20 2023 ce at 11:35:03.000 AM +0000",
459///     Err(FormattedDateTimePatternError::NamesNotLoaded(Field { symbol: FieldSymbol::Weekday(Weekday::Format), length: FieldLength::One }.into())),
460///     [
461///         (7, 10, Part::ERROR), // mon
462///         (7, 10, parts::WEEKDAY), // mon
463///         (11, 14, Part::ERROR), // M11
464///         (11, 14, parts::MONTH), // M11
465///         (15, 17, icu::decimal::parts::INTEGER), // 20
466///         (15, 17, parts::DAY), // 20
467///         (18, 22, icu::decimal::parts::INTEGER), // 2023
468///         (18, 22, parts::YEAR), // 2023
469///         (23, 25, Part::ERROR), // CE
470///         (23, 25, parts::ERA), // CE
471///         (29, 31, icu::decimal::parts::INTEGER), // 11
472///         (29, 31, parts::HOUR), // 11
473///         (32, 34, icu::decimal::parts::INTEGER), // 35
474///         (32, 34, parts::MINUTE), // 35
475///         (35, 41, parts::SECOND), // 03.000
476///         (35, 37, icu::decimal::parts::INTEGER), // 03
477///         (37, 38, icu::decimal::parts::DECIMAL), // .
478///         (38, 41, icu::decimal::parts::FRACTION), // 000
479///         (42, 44, Part::ERROR), // AM
480///         (42, 44, parts::DAY_PERIOD), // AM
481///         (45, 50, Part::ERROR), // +0000
482///         (45, 50, parts::TIME_ZONE_NAME), // +0000
483///     ]
484/// );
485///
486/// // To make the error occur sooner, one can use an EmptyDataProvider:
487/// let empty = EmptyDataProvider::new();
488/// assert!(matches!(
489///     names.load_for_pattern(&empty, &pattern),
490///     Err(PatternLoadError::Data(_, _)),
491/// ));
492/// ```
493///
494/// If the pattern contains fields inconsistent with the receiver, an error will occur:
495///
496/// ```
497/// use icu::calendar::Gregorian;
498/// use icu::datetime::pattern::FormattedDateTimePatternError;
499/// use icu::datetime::parts;
500/// use icu::datetime::pattern::FixedCalendarDateTimeNames;
501/// use icu::datetime::pattern::DateTimePattern;
502/// use icu::datetime::unchecked::MissingInputFieldKind;
503/// use icu::datetime::fieldsets::zone::LocalizedOffsetLong;
504/// use icu::locale::locale;
505/// use icu::datetime::input::{DateTime, TimeZoneInfo};
506/// use writeable::{Part, assert_try_writeable_parts_eq};
507///
508/// // Create an instance that can format abbreviated month, weekday, and day period names:
509/// let mut names: FixedCalendarDateTimeNames<Gregorian, LocalizedOffsetLong> =
510///     FixedCalendarDateTimeNames::try_new(locale!("en").into()).unwrap();
511///
512/// // Create a pattern from a pattern string:
513/// let pattern_str = "'It is:' E MMM d y G 'at' h:mm:ssSSS a zzzz";
514/// let pattern: DateTimePattern = pattern_str.parse().unwrap();
515///
516/// // The pattern string contains lots of symbols including "E", "MMM", and "a",
517/// // but the `FixedCalendarDateTimeNames` is configured to format only time zones!
518/// // Further, the time zone we provide doesn't contain any offset into!
519/// // Missing data is filled in on a best-effort basis, and an error is signaled.
520/// assert_try_writeable_parts_eq!(
521///     names.with_pattern_unchecked(&pattern).format(&TimeZoneInfo::unknown()),
522///     "It is: {E} {M} {d} {y} {G} at {h}:{m}:{s} {a} {z}",
523///     Err(FormattedDateTimePatternError::MissingInputField(MissingInputFieldKind::Weekday)),
524///     [
525///         (7, 10, Part::ERROR), // {E}
526///         (7, 10, parts::WEEKDAY), // {E}
527///         (11, 14, Part::ERROR), // {M}
528///         (11, 14, parts::MONTH), // {M}
529///         (15, 18, Part::ERROR), // {d}
530///         (15, 18, parts::DAY), // {d}
531///         (19, 22, Part::ERROR), // {y}
532///         (19, 22, parts::YEAR), // {y}
533///         (23, 26, Part::ERROR), // {G}
534///         (23, 26, parts::ERA), // {G}
535///         (30, 33, Part::ERROR), // {h}
536///         (30, 33, parts::HOUR), // {h}
537///         (34, 37, Part::ERROR), // {m}
538///         (34, 37, parts::MINUTE), // {m}
539///         (38, 41, Part::ERROR), // {s}
540///         (38, 41, parts::SECOND), // {s}
541///         (42, 45, Part::ERROR), // {a}
542///         (42, 45, parts::DAY_PERIOD), // {a}
543///         (46, 49, Part::ERROR), // {z}
544///         (46, 49, parts::TIME_ZONE_NAME), // {z}
545///     ]
546/// );
547/// ```
548///
549/// Multiple types of time zone data can be loaded into a [`FixedCalendarDateTimeNames`]:
550///
551/// ```
552/// use icu::datetime::pattern::FixedCalendarDateTimeNames;
553/// use icu::datetime::fieldsets::enums::ZoneFieldSet;
554/// use icu::locale::locale;
555/// use icu::datetime::input::{DateTime, Time};
556/// use writeable::assert_try_writeable_eq;
557///
558/// // Create an instance that can format abbreviated month, weekday, and day period names:
559/// let mut names: FixedCalendarDateTimeNames<(), ZoneFieldSet> =
560///     FixedCalendarDateTimeNames::try_new(locale!("uk").into()).unwrap();
561///
562/// // Load the names for generic short:
563/// names.include_time_zone_essentials().unwrap();
564/// names.include_time_zone_generic_short_names().unwrap();
565/// names.include_time_zone_location_names().unwrap();
566///
567/// // The same functions can be called a second time (nothing will happen):
568/// names.include_time_zone_essentials().unwrap();
569/// names.include_time_zone_generic_short_names().unwrap();
570/// names.include_time_zone_location_names().unwrap();
571///
572/// // We can load names for a different zone style:
573/// names.include_time_zone_generic_long_names().unwrap();
574/// ```
575///
576/// However, new time zone names cannot be added into a formatter that already has them. If you
577/// need this functionality, see <https://github.com/unicode-org/icu4x/issues/6063>
578///
579/// ```
580/// use icu::datetime::fieldsets::enums::ZoneFieldSet;
581/// use icu::datetime::fieldsets::zone;
582/// use icu::datetime::pattern::FixedCalendarDateTimeNames;
583/// use icu::datetime::NoCalendarFormatter;
584/// use icu::locale::locale;
585/// use icu_datetime::pattern::PatternLoadError;
586/// use icu_provider::DataError;
587/// use icu_provider::DataErrorKind;
588///
589/// let prefs = locale!("uk").into();
590///
591/// // Create a formatter for generic long time zones:
592/// let formatter =
593///     NoCalendarFormatter::try_new(prefs, zone::GenericLong).unwrap();
594///
595/// // Convert it to a FixedCalendarDateTimeNames:
596/// let mut names =
597///     FixedCalendarDateTimeNames::from_formatter(prefs, formatter)
598///         .cast_into_fset::<ZoneFieldSet>();
599///
600/// // Specific names cannot be added:
601/// assert!(matches!(
602///     names.include_time_zone_specific_long_names(),
603///     Err(PatternLoadError::Data(
604///         DataError {
605///             kind: DataErrorKind::InconsistentData(_),
606///             ..
607///         },
608///         _
609///     ))
610/// ));
611/// ```
612#[derive(Debug, Clone)]
613pub struct FixedCalendarDateTimeNames<C, FSet: DateTimeNamesMarker = CompositeDateTimeFieldSet> {
614    prefs: DateTimeFormatterPreferences,
615    inner: RawDateTimeNames<FSet>,
616    metadata: DateTimeNamesMetadata,
617    _calendar: PhantomData<C>,
618}
619
620/// Extra metadata associated with DateTimeNames but not DateTimeFormatter.
621#[derive(Debug, Clone)]
622pub(crate) struct DateTimeNamesMetadata {
623    zone_checksum: Option<u64>,
624}
625
626impl DateTimeNamesMetadata {
627    /// No impl Default: emphasize when we create a new empty instance
628    #[inline]
629    pub(crate) fn new_empty() -> Self {
630        Self {
631            zone_checksum: None,
632        }
633    }
634    /// If mz_periods is already populated, we can't load anything else because
635    /// we can't verify the checksum. Set a blank checksum in this case.
636    #[inline]
637    pub(crate) fn new_from_previous<M: DateTimeNamesMarker>(names: &RawDateTimeNames<M>) -> Self {
638        if names.mz_periods.get().inner.get_option().is_some() {
639            Self {
640                zone_checksum: Some(0),
641            }
642        } else {
643            Self::new_empty()
644        }
645    }
646}
647
648/// A low-level type that formats datetime patterns with localized names.
649/// The calendar is chosen in the constructor at runtime.
650///
651/// Currently this only supports loading of non-calendar-specific names, but
652/// additional functions may be added in the future. If you need this, see
653/// <https://github.com/unicode-org/icu4x/issues/6107>
654#[derive(Debug, Clone)]
655pub struct DateTimeNames<FSet: DateTimeNamesMarker> {
656    inner: FixedCalendarDateTimeNames<(), FSet>,
657    calendar: FormattableAnyCalendar,
658}
659
660pub(crate) struct RawDateTimeNames<FSet: DateTimeNamesMarker> {
661    year_names: <FSet::YearNames as NamesContainer<YearNamesV1, YearNameLength>>::Container,
662    month_names: <FSet::MonthNames as NamesContainer<MonthNamesV1, MonthNameLength>>::Container,
663    weekday_names:
664        <FSet::WeekdayNames as NamesContainer<WeekdayNamesV1, WeekdayNameLength>>::Container,
665    dayperiod_names:
666        <FSet::DayPeriodNames as NamesContainer<DayPeriodNamesV1, DayPeriodNameLength>>::Container,
667    zone_essentials: <FSet::ZoneEssentials as NamesContainer<tz::EssentialsV1, ()>>::Container,
668    locations_root: <FSet::ZoneLocationsRoot as NamesContainer<tz::LocationsRootV1, ()>>::Container,
669    locations: <FSet::ZoneLocations as NamesContainer<tz::LocationsOverrideV1, ()>>::Container,
670    exemplars_root: <FSet::ZoneExemplarsRoot as NamesContainer<tz::CitiesRootV1, ()>>::Container,
671    exemplars: <FSet::ZoneExemplars as NamesContainer<tz::CitiesOverrideV1, ()>>::Container,
672    mz_generic_long: <FSet::ZoneGenericLong as NamesContainer<tz::MzGenericLongV1, ()>>::Container,
673    mz_generic_short:
674        <FSet::ZoneGenericShort as NamesContainer<tz::MzGenericShortV1, ()>>::Container,
675    mz_standard_long:
676        <FSet::ZoneStandardLong as NamesContainer<tz::MzStandardLongV1, ()>>::Container,
677    mz_specific_long:
678        <FSet::ZoneSpecificLong as NamesContainer<tz::MzSpecificLongV1, ()>>::Container,
679    mz_specific_short:
680        <FSet::ZoneSpecificShort as NamesContainer<tz::MzSpecificShortV1, ()>>::Container,
681    mz_periods: <FSet::MetazoneLookup as NamesContainer<tz::MzPeriodV1, ()>>::Container,
682    // TODO(#4340): Make the DecimalFormatter optional
683    decimal_formatter: Option<DecimalFormatter>,
684    _marker: PhantomData<FSet>,
685}
686
687// Need a custom impl because not all of the associated types impl Debug
688impl<FSet: DateTimeNamesMarker> fmt::Debug for RawDateTimeNames<FSet> {
689    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
690        f.debug_struct("RawDateTimeNames")
691            .field("year_names", &self.year_names)
692            .field("month_names", &self.month_names)
693            .field("weekday_names", &self.weekday_names)
694            .field("dayperiod_names", &self.dayperiod_names)
695            .field("zone_essentials", &self.zone_essentials)
696            .field("locations_root", &self.locations_root)
697            .field("locations", &self.locations)
698            .field("exemplars_root", &self.exemplars_root)
699            .field("exemplars", &self.exemplars)
700            .field("mz_generic_long", &self.mz_generic_long)
701            .field("mz_generic_short", &self.mz_generic_short)
702            .field("mz_standard_long", &self.mz_standard_long)
703            .field("mz_specific_long", &self.mz_specific_long)
704            .field("mz_specific_short", &self.mz_specific_short)
705            .field("mz_periods", &self.mz_periods)
706            .field("decimal_formatter", &self.decimal_formatter)
707            .finish()
708    }
709}
710
711impl<FSet: DateTimeNamesMarker> Clone for RawDateTimeNames<FSet> {
712    fn clone(&self) -> Self {
713        Self {
714            year_names: self.year_names.clone(),
715            month_names: self.month_names.clone(),
716            weekday_names: self.weekday_names.clone(),
717            dayperiod_names: self.dayperiod_names.clone(),
718            zone_essentials: self.zone_essentials.clone(),
719            locations_root: self.locations_root.clone(),
720            locations: self.locations.clone(),
721            exemplars_root: self.exemplars_root.clone(),
722            exemplars: self.exemplars.clone(),
723            mz_generic_long: self.mz_generic_long.clone(),
724            mz_generic_short: self.mz_generic_short.clone(),
725            mz_standard_long: self.mz_standard_long.clone(),
726            mz_specific_long: self.mz_specific_long.clone(),
727            mz_specific_short: self.mz_specific_short.clone(),
728            mz_periods: self.mz_periods.clone(),
729            decimal_formatter: self.decimal_formatter.clone(),
730            _marker: PhantomData,
731        }
732    }
733}
734
735impl<FSet: DateTimeNamesMarker> RawDateTimeNames<FSet> {
736    pub(crate) fn cast_into_fset<FSet2: DateTimeNamesFrom<FSet>>(self) -> RawDateTimeNames<FSet2> {
737        RawDateTimeNames {
738            year_names: FSet2::map_year_names(self.year_names),
739            month_names: FSet2::map_month_names(self.month_names),
740            weekday_names: FSet2::map_weekday_names(self.weekday_names),
741            dayperiod_names: FSet2::map_day_period_names(self.dayperiod_names),
742            zone_essentials: FSet2::map_zone_essentials(self.zone_essentials),
743            locations_root: FSet2::map_zone_locations_root(self.locations_root),
744            locations: FSet2::map_zone_locations(self.locations),
745            exemplars_root: FSet2::map_zone_exemplars_root(self.exemplars_root),
746            exemplars: FSet2::map_zone_exemplars(self.exemplars),
747            mz_generic_long: FSet2::map_zone_generic_long(self.mz_generic_long),
748            mz_generic_short: FSet2::map_zone_generic_short(self.mz_generic_short),
749            mz_standard_long: FSet2::map_zone_standard_long(self.mz_standard_long),
750            mz_specific_long: FSet2::map_zone_specific_long(self.mz_specific_long),
751            mz_specific_short: FSet2::map_zone_specific_short(self.mz_specific_short),
752            mz_periods: FSet2::map_metazone_lookup(self.mz_periods),
753            decimal_formatter: self.decimal_formatter,
754            _marker: PhantomData,
755        }
756    }
757}
758
759#[derive(Debug, Copy, Clone)]
760pub(crate) struct RawDateTimeNamesBorrowed<'l> {
761    year_names: OptionalNames<YearNameLength, &'l YearNames<'l>>,
762    month_names: OptionalNames<MonthNameLength, &'l MonthNames<'l>>,
763    weekday_names: OptionalNames<WeekdayNameLength, &'l LinearNames<'l>>,
764    dayperiod_names: OptionalNames<DayPeriodNameLength, &'l LinearNames<'l>>,
765    zone_essentials: OptionalNames<(), &'l tz::Essentials<'l>>,
766    locations_root: OptionalNames<(), &'l tz::Locations<'l>>,
767    locations: OptionalNames<(), &'l tz::Locations<'l>>,
768    exemplars_root: OptionalNames<(), &'l tz::ExemplarCities<'l>>,
769    exemplars: OptionalNames<(), &'l tz::ExemplarCities<'l>>,
770    mz_generic_long: OptionalNames<(), &'l tz::MzGeneric<'l>>,
771    mz_standard_long: OptionalNames<(), &'l tz::MzGeneric<'l>>,
772    mz_generic_short: OptionalNames<(), &'l tz::MzGeneric<'l>>,
773    mz_specific_long: OptionalNames<(), &'l tz::MzSpecific<'l>>,
774    mz_specific_short: OptionalNames<(), &'l tz::MzSpecific<'l>>,
775    mz_periods: OptionalNames<(), &'l tz::MzPeriod<'l>>,
776    pub(crate) decimal_formatter: Option<&'l DecimalFormatter>,
777}
778
779impl<C, FSet: DateTimeNamesMarker> FixedCalendarDateTimeNames<C, FSet> {
780    /// Constructor that takes a selected locale and creates an empty instance.
781    ///
782    /// For an example, see [`FixedCalendarDateTimeNames`].
783    ///
784    /// ✨ *Enabled with the `compiled_data` Cargo feature.*
785    ///
786    /// [📚 Help choosing a constructor](icu_provider::constructors)
787    #[cfg(feature = "compiled_data")]
788    pub fn try_new(prefs: DateTimeFormatterPreferences) -> Result<Self, DataError> {
789        let mut names = Self {
790            prefs,
791            inner: RawDateTimeNames::new_without_number_formatting(),
792            metadata: DateTimeNamesMetadata::new_empty(), // OK: this is a constructor
793            _calendar: PhantomData,
794        };
795        names.include_decimal_formatter()?;
796        Ok(names)
797    }
798
799    #[doc = icu_provider::gen_buffer_unstable_docs!(UNSTABLE, Self::try_new)]
800    pub fn try_new_unstable<P>(
801        provider: &P,
802        prefs: DateTimeFormatterPreferences,
803    ) -> Result<Self, DataError>
804    where
805        P: DataProvider<DecimalSymbolsV1> + DataProvider<DecimalDigitsV1> + ?Sized,
806    {
807        let mut names = Self {
808            prefs,
809            inner: RawDateTimeNames::new_without_number_formatting(),
810            metadata: DateTimeNamesMetadata::new_empty(), // OK: this is a constructor
811            _calendar: PhantomData,
812        };
813        names.load_decimal_formatter(provider)?;
814        Ok(names)
815    }
816
817    icu_provider::gen_buffer_data_constructors!(
818        (prefs: DateTimeFormatterPreferences) -> error: DataError,
819        functions: [
820            try_new: skip,
821            try_new_with_buffer_provider,
822            try_new_unstable,
823            Self,
824        ]
825    );
826
827    /// Creates a completely empty instance, not even with number formatting.
828    ///
829    /// # Examples
830    ///
831    /// Errors occur if a number formatter is not loaded but one is required:
832    ///
833    /// ```
834    /// use icu::calendar::Gregorian;
835    /// use icu::datetime::input::Date;
836    /// use icu::datetime::parts;
837    /// use icu::datetime::pattern::FormattedDateTimePatternError;
838    /// use icu::datetime::pattern::FixedCalendarDateTimeNames;
839    /// use icu::datetime::pattern::DateTimePattern;
840    /// use icu::datetime::fieldsets::enums::DateFieldSet;
841    /// use icu::locale::locale;
842    /// use writeable::{Part, assert_try_writeable_parts_eq};
843    ///
844    /// // Create an instance that can format only date fields:
845    /// let names: FixedCalendarDateTimeNames<Gregorian, DateFieldSet> =
846    ///     FixedCalendarDateTimeNames::new_without_number_formatting(locale!("en").into());
847    ///
848    /// // Create a pattern from a pattern string:
849    /// let pattern_str = "'It is:' y-MM-dd";
850    /// let pattern: DateTimePattern = pattern_str.parse().unwrap();
851    ///
852    /// // The pattern string contains lots of numeric symbols,
853    /// // but we did not load any data!
854    ///
855    /// let date = Date::try_new_gregorian(2024, 7, 1).unwrap();
856    ///
857    /// // Missing data is filled in on a best-effort basis, and an error is signaled.
858    /// // (note that the padding is ignored in this fallback mode)
859    /// assert_try_writeable_parts_eq!(
860    ///     names.with_pattern_unchecked(&pattern).format(&date),
861    ///     "It is: 2024-07-01",
862    ///     Err(FormattedDateTimePatternError::DecimalFormatterNotLoaded),
863    ///     [
864    ///         (7, 11, Part::ERROR), // 2024
865    ///         (7, 11, parts::YEAR), // 2024
866    ///         (12, 14, Part::ERROR), // 07
867    ///         (12, 14, parts::MONTH), // 07
868    ///         (15, 17, Part::ERROR), // 01
869    ///         (15, 17, parts::DAY), // 01
870    ///     ]
871    /// );
872    /// ```
873    pub fn new_without_number_formatting(prefs: DateTimeFormatterPreferences) -> Self {
874        Self {
875            prefs,
876            inner: RawDateTimeNames::new_without_number_formatting(),
877            metadata: DateTimeNamesMetadata::new_empty(), // OK: this is a constructor
878            _calendar: PhantomData,
879        }
880    }
881}
882
883impl<C: CldrCalendar, FSet: DateTimeNamesMarker> FixedCalendarDateTimeNames<C, FSet> {
884    /// Creates an instance with the names loaded in a [`FixedCalendarDateTimeFormatter`].
885    ///
886    /// This function requires passing in the [`DateTimeFormatterPreferences`] because it is not
887    /// retained in the formatter. Pass the same value or else unexpected behavior may occur.
888    ///
889    /// # Examples
890    ///
891    /// ```
892    /// use icu::datetime::input::Date;
893    /// use icu::datetime::input::{DateTime, Time};
894    /// use icu::datetime::FixedCalendarDateTimeFormatter;
895    /// use icu::datetime::fieldsets::{YMD, YMDT};
896    /// use icu::datetime::pattern::{FixedCalendarDateTimeNames, DayPeriodNameLength};
897    /// use icu::locale::locale;
898    /// use writeable::assert_writeable_eq;
899    ///
900    /// let prefs = locale!("es-MX").into();
901    ///
902    /// let formatter =
903    ///     FixedCalendarDateTimeFormatter::try_new(
904    ///         prefs,
905    ///         YMD::long(),
906    ///     )
907    ///     .unwrap();
908    ///
909    /// assert_writeable_eq!(
910    ///     formatter.format(&Date::try_new_gregorian(2025, 2, 13).unwrap()),
911    ///     "13 de febrero de 2025"
912    /// );
913    ///
914    /// // Change the YMD formatter to a YMDT formatter, after loading day period names.
915    /// // This assumes that the locale uses Abbreviated names for the given semantic skeleton!
916    /// let mut names = FixedCalendarDateTimeNames::from_formatter(prefs, formatter).cast_into_fset::<YMDT>();
917    /// names.include_day_period_names(DayPeriodNameLength::Abbreviated).unwrap();
918    /// let formatter = names.try_into_formatter(YMD::long().with_time_hm()).unwrap();
919    ///
920    /// assert_writeable_eq!(
921    ///     formatter.format(&DateTime {
922    ///         date: Date::try_new_gregorian(2025, 2, 13).unwrap(),
923    ///         time: Time::start_of_day(),
924    ///     }),
925    ///     "13 de febrero de 2025 a las 12:00 a.m."
926    /// );
927    /// ```
928    pub fn from_formatter(
929        prefs: DateTimeFormatterPreferences,
930        formatter: FixedCalendarDateTimeFormatter<C, FSet>,
931    ) -> Self {
932        let metadata = DateTimeNamesMetadata::new_from_previous(&formatter.names);
933        Self {
934            prefs,
935            inner: formatter.names,
936            metadata,
937            _calendar: PhantomData,
938        }
939    }
940
941    fn from_parts(
942        prefs: DateTimeFormatterPreferences,
943        parts: (RawDateTimeNames<FSet>, DateTimeNamesMetadata),
944    ) -> Self {
945        Self {
946            prefs,
947            inner: parts.0,
948            metadata: parts.1,
949            _calendar: PhantomData,
950        }
951    }
952}
953
954impl<C: CldrCalendar, FSet: DateTimeMarkers> FixedCalendarDateTimeNames<C, FSet>
955where
956    FSet::D: TypedDateDataMarkers<C>,
957    FSet::T: TimeMarkers,
958    FSet::Z: ZoneMarkers,
959    FSet: GetField<CompositeFieldSet>,
960{
961    /// Loads a pattern for the given field set with compiled data and returns a [`FixedCalendarDateTimeFormatter`].
962    ///
963    /// The names in the current [`FixedCalendarDateTimeNames`] _must_ be sufficient for the field set.
964    /// If not, the input object will be returned with an error.
965    ///
966    /// ✨ *Enabled with the `compiled_data` Cargo feature.*
967    ///
968    /// [📚 Help choosing a constructor](icu_provider::constructors)
969    ///
970    /// # Examples
971    ///
972    /// ```
973    /// use icu::datetime::fieldsets::T;
974    /// use icu::datetime::input::Time;
975    /// use icu::datetime::pattern::{
976    ///     DayPeriodNameLength, FixedCalendarDateTimeNames,
977    /// };
978    /// use icu::locale::locale;
979    /// use writeable::assert_writeable_eq;
980    ///
981    /// let names =
982    ///     FixedCalendarDateTimeNames::<(), _>::new_without_number_formatting(
983    ///         locale!("es-MX").into(),
984    ///     );
985    ///
986    /// let field_set = T::hm();
987    ///
988    /// // Cannot convert yet: no names are loaded
989    /// let mut names = names.try_into_formatter(field_set).unwrap_err().1;
990    ///
991    /// // Load the data we need:
992    /// names
993    ///     .include_day_period_names(DayPeriodNameLength::Abbreviated)
994    ///     .unwrap();
995    /// names.include_decimal_formatter().unwrap();
996    ///
997    /// // Now the conversion is successful:
998    /// let formatter = names.try_into_formatter(field_set).unwrap();
999    ///
1000    /// assert_writeable_eq!(formatter.format(&Time::start_of_day()), "12:00 a.m.");
1001    /// ```
1002    #[expect(clippy::result_large_err)] // returning self as the error
1003    #[cfg(feature = "compiled_data")]
1004    pub fn try_into_formatter(
1005        self,
1006        field_set: FSet,
1007    ) -> Result<FixedCalendarDateTimeFormatter<C, FSet>, (DateTimeFormatterLoadError, Self)>
1008    where
1009        crate::provider::Baked: AllFixedCalendarPatternDataMarkers<C, FSet>,
1010    {
1011        FixedCalendarDateTimeFormatter::try_new_internal_with_names(
1012            &crate::provider::Baked,
1013            &EmptyDataProvider,
1014            &ExternalLoaderUnstable(&EmptyDataProvider), // for decimals only
1015            self.prefs,
1016            field_set.get_field(),
1017            self.inner,
1018            self.metadata,
1019        )
1020        .map_err(|e| (e.0, Self::from_parts(self.prefs, e.1)))
1021    }
1022
1023    #[doc = icu_provider::gen_buffer_unstable_docs!(UNSTABLE, Self::try_into_formatter)]
1024    #[expect(clippy::result_large_err)] // returning self as the error
1025    pub fn try_into_formatter_unstable<P>(
1026        self,
1027        provider: &P,
1028        field_set: FSet,
1029    ) -> Result<FixedCalendarDateTimeFormatter<C, FSet>, (DateTimeFormatterLoadError, Self)>
1030    where
1031        P: AllFixedCalendarPatternDataMarkers<C, FSet> + ?Sized,
1032    {
1033        FixedCalendarDateTimeFormatter::try_new_internal_with_names(
1034            provider,
1035            &EmptyDataProvider,
1036            &ExternalLoaderUnstable(&EmptyDataProvider), // for decimals only
1037            self.prefs,
1038            field_set.get_field(),
1039            self.inner,
1040            self.metadata,
1041        )
1042        .map_err(|e| (e.0, Self::from_parts(self.prefs, e.1)))
1043    }
1044
1045    #[doc = icu_provider::gen_buffer_unstable_docs!(BUFFER, Self::try_into_formatter)]
1046    #[expect(clippy::result_large_err)] // returning self as the error
1047    #[cfg(feature = "serde")]
1048    pub fn try_into_formatter_with_buffer_provider<P>(
1049        self,
1050        provider: &P,
1051        field_set: FSet,
1052    ) -> Result<FixedCalendarDateTimeFormatter<C, FSet>, (DateTimeFormatterLoadError, Self)>
1053    where
1054        P: BufferProvider + ?Sized,
1055    {
1056        FixedCalendarDateTimeFormatter::try_new_internal_with_names(
1057            &provider.as_deserializing(),
1058            &EmptyDataProvider,
1059            &ExternalLoaderUnstable(&EmptyDataProvider), // for decimals only
1060            self.prefs,
1061            field_set.get_field(),
1062            self.inner,
1063            self.metadata,
1064        )
1065        .map_err(|e| (e.0, Self::from_parts(self.prefs, e.1)))
1066    }
1067}
1068
1069impl<FSet: DateTimeNamesMarker> DateTimeNames<FSet> {
1070    /// Creates a completely empty instance, not even with number formatting,
1071    /// with the specified calendar.
1072    pub fn try_new_with_calendar_without_number_formatting(
1073        prefs: DateTimeFormatterPreferences,
1074        calendar: AnyCalendar,
1075    ) -> Result<Self, UnsupportedCalendarError> {
1076        let kind = calendar.kind();
1077        let calendar = FormattableAnyCalendar::try_from_any_calendar(calendar)
1078            .ok_or(UnsupportedCalendarError { kind })?;
1079        Ok(Self {
1080            inner: FixedCalendarDateTimeNames::new_without_number_formatting(prefs),
1081            calendar,
1082        })
1083    }
1084
1085    /// Creates an instance with the names and calendar loaded in a [`DateTimeFormatter`].
1086    ///
1087    /// This function requires passing in the [`DateTimeFormatterPreferences`] because it is not
1088    /// retained in the formatter. Pass the same value or else unexpected behavior may occur.
1089    ///
1090    /// # Examples
1091    ///
1092    /// ```
1093    /// use icu::datetime::input::Date;
1094    /// use icu::datetime::input::{DateTime, Time};
1095    /// use icu::datetime::DateTimeFormatter;
1096    /// use icu::datetime::fieldsets::{YMD, YMDT};
1097    /// use icu::datetime::pattern::{DateTimeNames, DayPeriodNameLength};
1098    /// use icu::locale::locale;
1099    /// use writeable::assert_writeable_eq;
1100    ///
1101    /// let prefs = locale!("es-MX").into();
1102    ///
1103    /// let formatter =
1104    ///     DateTimeFormatter::try_new(
1105    ///         prefs,
1106    ///         YMD::long(),
1107    ///     )
1108    ///     .unwrap();
1109    ///
1110    /// assert_writeable_eq!(
1111    ///     formatter.format(&Date::try_new_iso(2025, 2, 13).unwrap()),
1112    ///     "13 de febrero de 2025"
1113    /// );
1114    ///
1115    /// // Change the YMD formatter to a YMDT formatter, after loading day period names.
1116    /// // This assumes that the locale uses Abbreviated names for the given semantic skeleton!
1117    /// let mut names = DateTimeNames::from_formatter(prefs, formatter).cast_into_fset::<YMDT>();
1118    /// names.as_mut().include_day_period_names(DayPeriodNameLength::Abbreviated).unwrap();
1119    /// let formatter = names.try_into_formatter(YMD::long().with_time_hm()).unwrap();
1120    ///
1121    /// assert_writeable_eq!(
1122    ///     formatter.format(&DateTime {
1123    ///         date: Date::try_new_iso(2025, 2, 13).unwrap(),
1124    ///         time: Time::start_of_day(),
1125    ///     }),
1126    ///     "13 de febrero de 2025 a las 12:00 a.m."
1127    /// );
1128    /// ```
1129    pub fn from_formatter(
1130        prefs: DateTimeFormatterPreferences,
1131        formatter: DateTimeFormatter<FSet>,
1132    ) -> Self {
1133        let metadata = DateTimeNamesMetadata::new_from_previous(&formatter.names);
1134        Self::from_parts(
1135            prefs,
1136            (formatter.calendar.into_tagged(), formatter.names, metadata),
1137        )
1138    }
1139
1140    fn from_parts(
1141        prefs: DateTimeFormatterPreferences,
1142        parts: (
1143            FormattableAnyCalendar,
1144            RawDateTimeNames<FSet>,
1145            DateTimeNamesMetadata,
1146        ),
1147    ) -> Self {
1148        Self {
1149            inner: FixedCalendarDateTimeNames {
1150                prefs,
1151                inner: parts.1,
1152                metadata: parts.2,
1153                _calendar: PhantomData,
1154            },
1155            calendar: parts.0,
1156        }
1157    }
1158}
1159
1160impl<FSet: DateTimeMarkers> DateTimeNames<FSet>
1161where
1162    FSet::D: DateDataMarkers,
1163    FSet::T: TimeMarkers,
1164    FSet::Z: ZoneMarkers,
1165    FSet: GetField<CompositeFieldSet>,
1166{
1167    /// Loads a pattern for the given field set with compiled data and returns a [`DateTimeFormatter`].
1168    ///
1169    /// The names in the current [`DateTimeNames`] _must_ be sufficient for the field set.
1170    /// If not, the input object will be returned with an error.
1171    ///
1172    /// ✨ *Enabled with the `compiled_data` Cargo feature.*
1173    ///
1174    /// [📚 Help choosing a constructor](icu_provider::constructors)
1175    ///
1176    /// # Examples
1177    ///
1178    /// ```
1179    /// use icu::calendar::{AnyCalendar, AnyCalendarKind};
1180    /// use icu::datetime::fieldsets::T;
1181    /// use icu::datetime::input::Time;
1182    /// use icu::datetime::pattern::{DateTimeNames, DayPeriodNameLength};
1183    /// use icu::locale::locale;
1184    /// use writeable::assert_writeable_eq;
1185    ///
1186    /// let kind = AnyCalendarKind::new(locale!("es-MX").into());
1187    /// let calendar = AnyCalendar::new(kind);
1188    ///
1189    /// let names = DateTimeNames::try_new_with_calendar_without_number_formatting(
1190    ///     locale!("es-MX").into(),
1191    ///     calendar,
1192    /// )
1193    /// .expect("All locale-default calendars are supported");
1194    ///
1195    /// let field_set = T::hm();
1196    ///
1197    /// // Cannot convert yet: no names are loaded
1198    /// let mut names = names.try_into_formatter(field_set).unwrap_err().1;
1199    ///
1200    /// // Load the data we need:
1201    /// names
1202    ///     .as_mut()
1203    ///     .include_day_period_names(DayPeriodNameLength::Abbreviated)
1204    ///     .unwrap();
1205    /// names.as_mut().include_decimal_formatter().unwrap();
1206    ///
1207    /// // Now the conversion is successful:
1208    /// let formatter = names.try_into_formatter(field_set).unwrap();
1209    ///
1210    /// assert_writeable_eq!(formatter.format(&Time::start_of_day()), "12:00 a.m.");
1211    /// ```
1212    #[expect(clippy::result_large_err)] // returning self as the error
1213    #[cfg(feature = "compiled_data")]
1214    pub fn try_into_formatter(
1215        self,
1216        field_set: FSet,
1217    ) -> Result<DateTimeFormatter<FSet>, (DateTimeFormatterLoadError, Self)>
1218    where
1219        crate::provider::Baked: AllAnyCalendarPatternDataMarkers<FSet>,
1220    {
1221        DateTimeFormatter::try_new_internal_with_calendar_and_names(
1222            &crate::provider::Baked,
1223            &EmptyDataProvider,
1224            &ExternalLoaderUnstable(&EmptyDataProvider), // for decimals only
1225            self.inner.prefs,
1226            field_set.get_field(),
1227            self.calendar,
1228            self.inner.inner,
1229            self.inner.metadata,
1230        )
1231        .map_err(|e| (e.0, Self::from_parts(self.inner.prefs, e.1)))
1232    }
1233
1234    #[doc = icu_provider::gen_buffer_unstable_docs!(UNSTABLE, Self::try_into_formatter)]
1235    #[expect(clippy::result_large_err)] // returning self as the error
1236    pub fn try_into_formatter_unstable<P>(
1237        self,
1238        provider: &P,
1239        field_set: FSet,
1240    ) -> Result<DateTimeFormatter<FSet>, (DateTimeFormatterLoadError, Self)>
1241    where
1242        P: AllAnyCalendarPatternDataMarkers<FSet> + ?Sized,
1243    {
1244        DateTimeFormatter::try_new_internal_with_calendar_and_names(
1245            provider,
1246            &EmptyDataProvider,
1247            &ExternalLoaderUnstable(&EmptyDataProvider), // for decimals only
1248            self.inner.prefs,
1249            field_set.get_field(),
1250            self.calendar,
1251            self.inner.inner,
1252            self.inner.metadata,
1253        )
1254        .map_err(|e| (e.0, Self::from_parts(self.inner.prefs, e.1)))
1255    }
1256
1257    #[doc = icu_provider::gen_buffer_unstable_docs!(BUFFER, Self::try_into_formatter)]
1258    #[expect(clippy::result_large_err)] // returning self as the error
1259    #[cfg(feature = "serde")]
1260    pub fn try_into_formatter_with_buffer_provider<P>(
1261        self,
1262        provider: &P,
1263        field_set: FSet,
1264    ) -> Result<DateTimeFormatter<FSet>, (DateTimeFormatterLoadError, Self)>
1265    where
1266        P: BufferProvider + ?Sized,
1267    {
1268        DateTimeFormatter::try_new_internal_with_calendar_and_names(
1269            &provider.as_deserializing(),
1270            &EmptyDataProvider,
1271            &ExternalLoaderUnstable(&EmptyDataProvider), // for decimals only
1272            self.inner.prefs,
1273            field_set.get_field(),
1274            self.calendar,
1275            self.inner.inner,
1276            self.inner.metadata,
1277        )
1278        .map_err(|e| (e.0, Self::from_parts(self.inner.prefs, e.1)))
1279    }
1280}
1281
1282impl<FSet: DateTimeNamesMarker> AsRef<FixedCalendarDateTimeNames<(), FSet>>
1283    for DateTimeNames<FSet>
1284{
1285    fn as_ref(&self) -> &FixedCalendarDateTimeNames<(), FSet> {
1286        &self.inner
1287    }
1288}
1289
1290impl<FSet: DateTimeNamesMarker> AsMut<FixedCalendarDateTimeNames<(), FSet>>
1291    for DateTimeNames<FSet>
1292{
1293    fn as_mut(&mut self) -> &mut FixedCalendarDateTimeNames<(), FSet> {
1294        &mut self.inner
1295    }
1296}
1297
1298impl<C: CldrCalendar, FSet: DateTimeNamesMarker> FixedCalendarDateTimeNames<C, FSet> {
1299    /// Loads year (era or cycle) names for the specified length.
1300    ///
1301    /// Does not support multiple field symbols or lengths. See [#4337](https://github.com/unicode-org/icu4x/issues/4337)
1302    pub fn load_year_names<P>(
1303        &mut self,
1304        provider: &P,
1305        length: YearNameLength,
1306    ) -> Result<&mut Self, PatternLoadError>
1307    where
1308        P: DataProvider<C::YearNamesV1> + ?Sized,
1309    {
1310        self.inner.load_year_names(
1311            &C::YearNamesV1::bind(provider),
1312            self.prefs,
1313            length,
1314            length.to_approximate_error_field(),
1315        )?;
1316        Ok(self)
1317    }
1318
1319    /// Includes year (era or cycle) names for the specified length with compiled data.
1320    ///
1321    /// Does not support multiple field symbols or lengths. See [#4337](https://github.com/unicode-org/icu4x/issues/4337)
1322    ///
1323    /// ✨ *Enabled with the `compiled_data` Cargo feature.*
1324    ///
1325    /// # Examples
1326    ///
1327    /// ```
1328    /// use icu::calendar::Gregorian;
1329    /// use icu::datetime::pattern::FixedCalendarDateTimeNames;
1330    /// use icu::datetime::pattern::PatternLoadError;
1331    /// use icu::datetime::pattern::YearNameLength;
1332    /// use icu::locale::locale;
1333    ///
1334    /// let mut names =
1335    ///     FixedCalendarDateTimeNames::<Gregorian>::try_new(locale!("und").into())
1336    ///         .unwrap();
1337    ///
1338    /// // First length is successful:
1339    /// names.include_year_names(YearNameLength::Wide).unwrap();
1340    ///
1341    /// // Attempting to load the first length a second time will succeed:
1342    /// names.include_year_names(YearNameLength::Wide).unwrap();
1343    ///
1344    /// // But loading a new length fails:
1345    /// assert!(matches!(
1346    ///     names.include_year_names(YearNameLength::Abbreviated),
1347    ///     Err(PatternLoadError::ConflictingField { .. })
1348    /// ));
1349    /// ```
1350    #[cfg(feature = "compiled_data")]
1351    pub fn include_year_names(
1352        &mut self,
1353        length: YearNameLength,
1354    ) -> Result<&mut Self, PatternLoadError>
1355    where
1356        crate::provider::Baked: icu_provider::DataProvider<<C as CldrCalendar>::YearNamesV1>,
1357    {
1358        self.load_year_names(&crate::provider::Baked, length)
1359    }
1360
1361    /// Loads month names for the specified symbol and length.
1362    ///
1363    /// Does not support multiple field symbols or lengths. See [#4337](https://github.com/unicode-org/icu4x/issues/4337)
1364    pub fn load_month_names<P>(
1365        &mut self,
1366        provider: &P,
1367        length: MonthNameLength,
1368    ) -> Result<&mut Self, PatternLoadError>
1369    where
1370        P: DataProvider<C::MonthNamesV1> + ?Sized,
1371    {
1372        self.inner.load_month_names(
1373            &C::MonthNamesV1::bind(provider),
1374            self.prefs,
1375            length,
1376            length.to_approximate_error_field(),
1377        )?;
1378        Ok(self)
1379    }
1380
1381    /// Includes month names for the specified symbol and length with compiled data.
1382    ///
1383    /// Does not support multiple field symbols or lengths. See [#4337](https://github.com/unicode-org/icu4x/issues/4337)
1384    ///
1385    /// ✨ *Enabled with the `compiled_data` Cargo feature.*
1386    ///
1387    /// # Examples
1388    ///
1389    /// ```
1390    /// use icu::calendar::Gregorian;
1391    /// use icu::datetime::pattern::FixedCalendarDateTimeNames;
1392    /// use icu::datetime::pattern::MonthNameLength;
1393    /// use icu::datetime::pattern::PatternLoadError;
1394    /// use icu::locale::locale;
1395    ///
1396    /// let mut names =
1397    ///     FixedCalendarDateTimeNames::<Gregorian>::try_new(locale!("und").into())
1398    ///         .unwrap();
1399    ///
1400    /// // First length is successful:
1401    /// names.include_month_names(MonthNameLength::Wide).unwrap();
1402    ///
1403    /// // Attempting to load the first length a second time will succeed:
1404    /// names.include_month_names(MonthNameLength::Wide).unwrap();
1405    ///
1406    /// // But loading a new symbol or length fails:
1407    /// assert!(matches!(
1408    ///     names.include_month_names(MonthNameLength::StandaloneWide),
1409    ///     Err(PatternLoadError::ConflictingField { .. })
1410    /// ));
1411    /// assert!(matches!(
1412    ///     names.include_month_names(MonthNameLength::Abbreviated),
1413    ///     Err(PatternLoadError::ConflictingField { .. })
1414    /// ));
1415    /// ```
1416    #[cfg(feature = "compiled_data")]
1417    pub fn include_month_names(
1418        &mut self,
1419        length: MonthNameLength,
1420    ) -> Result<&mut Self, PatternLoadError>
1421    where
1422        crate::provider::Baked: icu_provider::DataProvider<<C as CldrCalendar>::MonthNamesV1>,
1423    {
1424        self.load_month_names(&crate::provider::Baked, length)
1425    }
1426}
1427
1428impl<C, FSet: DateTimeNamesMarker> FixedCalendarDateTimeNames<C, FSet> {
1429    /// Loads day period names for the specified length.
1430    ///
1431    /// Does not support multiple field symbols or lengths. See [#4337](https://github.com/unicode-org/icu4x/issues/4337)
1432    pub fn load_day_period_names<P>(
1433        &mut self,
1434        provider: &P,
1435        length: DayPeriodNameLength,
1436    ) -> Result<&mut Self, PatternLoadError>
1437    where
1438        P: DataProvider<DayPeriodNamesV1> + ?Sized,
1439    {
1440        let provider = DayPeriodNamesV1::bind(provider);
1441        self.inner.load_day_period_names(
1442            &provider,
1443            self.prefs,
1444            length,
1445            length.to_approximate_error_field(),
1446        )?;
1447        Ok(self)
1448    }
1449
1450    /// Includes day period names for the specified length with compiled data.
1451    ///
1452    /// Does not support multiple field symbols or lengths. See [#4337](https://github.com/unicode-org/icu4x/issues/4337)
1453    ///
1454    /// ✨ *Enabled with the `compiled_data` Cargo feature.*
1455    ///
1456    /// # Examples
1457    ///
1458    /// ```
1459    /// use icu::calendar::Gregorian;
1460    /// use icu::datetime::pattern::DayPeriodNameLength;
1461    /// use icu::datetime::pattern::FixedCalendarDateTimeNames;
1462    /// use icu::datetime::pattern::PatternLoadError;
1463    /// use icu::locale::locale;
1464    ///
1465    /// let mut names =
1466    ///     FixedCalendarDateTimeNames::<Gregorian>::try_new(locale!("und").into())
1467    ///         .unwrap();
1468    ///
1469    /// // First length is successful:
1470    /// names
1471    ///     .include_day_period_names(DayPeriodNameLength::Wide)
1472    ///     .unwrap();
1473    ///
1474    /// // Attempting to load the first length a second time will succeed:
1475    /// names
1476    ///     .include_day_period_names(DayPeriodNameLength::Wide)
1477    ///     .unwrap();
1478    ///
1479    /// // But loading a new length fails:
1480    /// assert!(matches!(
1481    ///     names.include_day_period_names(DayPeriodNameLength::Abbreviated),
1482    ///     Err(PatternLoadError::ConflictingField { .. })
1483    /// ));
1484    /// ```
1485    #[cfg(feature = "compiled_data")]
1486    pub fn include_day_period_names(
1487        &mut self,
1488        length: DayPeriodNameLength,
1489    ) -> Result<&mut Self, PatternLoadError> {
1490        self.load_day_period_names(&crate::provider::Baked, length)
1491    }
1492
1493    /// Loads weekday names for the specified symbol and length.
1494    ///
1495    /// Does not support multiple field symbols or lengths. See [#4337](https://github.com/unicode-org/icu4x/issues/4337)
1496    pub fn load_weekday_names<P>(
1497        &mut self,
1498        provider: &P,
1499        length: WeekdayNameLength,
1500    ) -> Result<&mut Self, PatternLoadError>
1501    where
1502        P: DataProvider<WeekdayNamesV1> + ?Sized,
1503    {
1504        self.inner.load_weekday_names(
1505            &WeekdayNamesV1::bind(provider),
1506            self.prefs,
1507            length,
1508            length.to_approximate_error_field(),
1509        )?;
1510        Ok(self)
1511    }
1512
1513    /// Includes weekday names for the specified symbol and length with compiled data.
1514    ///
1515    /// Does not support multiple field symbols or lengths. See [#4337](https://github.com/unicode-org/icu4x/issues/4337)
1516    ///
1517    /// ✨ *Enabled with the `compiled_data` Cargo feature.*
1518    ///
1519    /// # Examples
1520    ///
1521    /// ```
1522    /// use icu::calendar::Gregorian;
1523    /// use icu::datetime::pattern::FixedCalendarDateTimeNames;
1524    /// use icu::datetime::pattern::PatternLoadError;
1525    /// use icu::datetime::pattern::WeekdayNameLength;
1526    /// use icu::locale::locale;
1527    ///
1528    /// let mut names =
1529    ///     FixedCalendarDateTimeNames::<Gregorian>::try_new(locale!("und").into())
1530    ///         .unwrap();
1531    ///
1532    /// // First length is successful:
1533    /// names
1534    ///     .include_weekday_names(WeekdayNameLength::Wide)
1535    ///     .unwrap();
1536    ///
1537    /// // Attempting to load the first length a second time will succeed:
1538    /// names
1539    ///     .include_weekday_names(WeekdayNameLength::Wide)
1540    ///     .unwrap();
1541    ///
1542    /// // But loading a new symbol or length fails:
1543    /// assert!(matches!(
1544    ///     names.include_weekday_names(WeekdayNameLength::StandaloneWide),
1545    ///     Err(PatternLoadError::ConflictingField { .. })
1546    /// ));
1547    /// assert!(matches!(
1548    ///     names.include_weekday_names(WeekdayNameLength::Abbreviated),
1549    ///     Err(PatternLoadError::ConflictingField { .. })
1550    /// ));
1551    /// ```
1552    #[cfg(feature = "compiled_data")]
1553    pub fn include_weekday_names(
1554        &mut self,
1555        length: WeekdayNameLength,
1556    ) -> Result<&mut Self, PatternLoadError> {
1557        self.load_weekday_names(&crate::provider::Baked, length)
1558    }
1559
1560    /// Loads shared essential patterns for time zone formatting.
1561    pub fn load_time_zone_essentials<P>(
1562        &mut self,
1563        provider: &P,
1564    ) -> Result<&mut Self, PatternLoadError>
1565    where
1566        P: DataProvider<tz::EssentialsV1> + ?Sized,
1567    {
1568        self.inner
1569            .load_time_zone_essentials(&tz::EssentialsV1::bind(provider), self.prefs)?;
1570        Ok(self)
1571    }
1572
1573    /// Includes shared essential patterns for time zone formatting with compiled data.
1574    ///
1575    /// This data should always be loaded when performing time zone formatting.
1576    /// By itself, it allows localized offset formats.
1577    ///
1578    /// ✨ *Enabled with the `compiled_data` Cargo feature.*
1579    ///
1580    /// # Examples
1581    ///
1582    /// ```
1583    /// use icu::calendar::Gregorian;
1584    /// use icu::datetime::fieldsets::enums::ZoneFieldSet;
1585    /// use icu::datetime::input::ZonedDateTime;
1586    /// use icu::datetime::pattern::DateTimePattern;
1587    /// use icu::datetime::pattern::FixedCalendarDateTimeNames;
1588    /// use icu::locale::locale;
1589    /// use icu::time::zone::IanaParser;
1590    /// use writeable::assert_try_writeable_eq;
1591    ///
1592    /// let mut zone_london_winter = ZonedDateTime::try_strict_from_str(
1593    ///     "2024-01-01T00:00:00+00:00[Europe/London]",
1594    ///     Gregorian,
1595    ///     IanaParser::new(),
1596    /// )
1597    /// .unwrap()
1598    /// .zone;
1599    /// let mut zone_london_summer = ZonedDateTime::try_strict_from_str(
1600    ///     "2024-07-01T00:00:00+01:00[Europe/London]",
1601    ///     Gregorian,
1602    ///     IanaParser::new(),
1603    /// )
1604    /// .unwrap()
1605    /// .zone;
1606    ///
1607    /// let mut names =
1608    ///     FixedCalendarDateTimeNames::<Gregorian, ZoneFieldSet>::try_new(
1609    ///         locale!("en-GB").into(),
1610    ///     )
1611    ///     .unwrap();
1612    ///
1613    /// names.include_time_zone_essentials().unwrap();
1614    ///
1615    /// // Create a pattern with symbol `OOOO`:
1616    /// let pattern_str = "'Your time zone is:' OOOO";
1617    /// let pattern: DateTimePattern = pattern_str.parse().unwrap();
1618    ///
1619    /// assert_try_writeable_eq!(
1620    ///     names
1621    ///         .with_pattern_unchecked(&pattern)
1622    ///         .format(&zone_london_winter),
1623    ///     "Your time zone is: GMT+00:00",
1624    /// );
1625    /// assert_try_writeable_eq!(
1626    ///     names
1627    ///         .with_pattern_unchecked(&pattern)
1628    ///         .format(&zone_london_summer),
1629    ///     "Your time zone is: GMT+01:00",
1630    /// );
1631    ///
1632    /// // Now try `V`:
1633    /// let pattern_str = "'Your time zone is:' V";
1634    /// let pattern: DateTimePattern = pattern_str.parse().unwrap();
1635    ///
1636    /// assert_try_writeable_eq!(
1637    ///     names
1638    ///         .with_pattern_unchecked(&pattern)
1639    ///         .format(&zone_london_winter),
1640    ///     "Your time zone is: gblon",
1641    /// );
1642    ///
1643    /// // Now try `Z`:
1644    /// let pattern_str = "'Your time zone is:' Z";
1645    /// let pattern: DateTimePattern = pattern_str.parse().unwrap();
1646    ///
1647    /// assert_try_writeable_eq!(
1648    ///     names
1649    ///         .with_pattern_unchecked(&pattern)
1650    ///         .format(&zone_london_winter),
1651    ///     "Your time zone is: +0000",
1652    /// );
1653    ///
1654    /// // Now try `ZZZZZ`:
1655    /// let pattern_str = "'Your time zone is:' ZZZZZ";
1656    /// let pattern: DateTimePattern = pattern_str.parse().unwrap();
1657    ///
1658    /// assert_try_writeable_eq!(
1659    ///     names
1660    ///         .with_pattern_unchecked(&pattern)
1661    ///         .format(&zone_london_winter),
1662    ///     "Your time zone is: Z",
1663    /// );
1664    /// assert_try_writeable_eq!(
1665    ///     names
1666    ///         .with_pattern_unchecked(&pattern)
1667    ///         .format(&zone_london_summer),
1668    ///     "Your time zone is: +01:00",
1669    /// );
1670    /// ```
1671    #[cfg(feature = "compiled_data")]
1672    pub fn include_time_zone_essentials(&mut self) -> Result<&mut Self, PatternLoadError> {
1673        self.load_time_zone_essentials(&crate::provider::Baked)
1674    }
1675
1676    /// Loads location names for time zone formatting.
1677    pub fn load_time_zone_location_names<P>(
1678        &mut self,
1679        provider: &P,
1680    ) -> Result<&mut Self, PatternLoadError>
1681    where
1682        P: DataProvider<tz::LocationsOverrideV1> + DataProvider<tz::LocationsRootV1> + ?Sized,
1683    {
1684        self.inner.load_time_zone_location_names(
1685            &tz::LocationsOverrideV1::bind(provider),
1686            &tz::LocationsRootV1::bind(provider),
1687            self.prefs,
1688        )?;
1689        Ok(self)
1690    }
1691
1692    /// Includes location names for time zone formatting with compiled data.
1693    ///
1694    /// Important: When performing manual time zone data loading, in addition to the
1695    /// specific time zone format data, also call either:
1696    ///
1697    /// - [`FixedCalendarDateTimeNames::include_time_zone_essentials`]
1698    /// - [`FixedCalendarDateTimeNames::load_time_zone_essentials`]
1699    ///
1700    /// ✨ *Enabled with the `compiled_data` Cargo feature.*
1701    ///
1702    /// # Examples
1703    ///
1704    /// ```
1705    /// use icu::calendar::Gregorian;
1706    /// use icu::datetime::fieldsets::enums::ZoneFieldSet;
1707    /// use icu::datetime::input::ZonedDateTime;
1708    /// use icu::datetime::pattern::DateTimePattern;
1709    /// use icu::datetime::pattern::FixedCalendarDateTimeNames;
1710    /// use icu::locale::locale;
1711    /// use icu::time::zone::IanaParser;
1712    /// use writeable::assert_try_writeable_eq;
1713    ///
1714    /// let mut zone_london_winter = ZonedDateTime::try_strict_from_str(
1715    ///     "2024-01-01T00:00:00+00:00[Europe/London]",
1716    ///     Gregorian,
1717    ///     IanaParser::new(),
1718    /// )
1719    /// .unwrap()
1720    /// .zone;
1721    ///
1722    /// let mut names =
1723    ///     FixedCalendarDateTimeNames::<Gregorian, ZoneFieldSet>::try_new(
1724    ///         locale!("en-GB").into(),
1725    ///     )
1726    ///     .unwrap();
1727    ///
1728    /// names.include_time_zone_essentials().unwrap();
1729    /// names.include_time_zone_location_names().unwrap();
1730    ///
1731    /// // Try `VVVV`:
1732    /// let pattern_str = "'Your time zone is:' VVVV";
1733    /// let pattern: DateTimePattern = pattern_str.parse().unwrap();
1734    ///
1735    /// assert_try_writeable_eq!(
1736    ///     names
1737    ///         .with_pattern_unchecked(&pattern)
1738    ///         .format(&zone_london_winter),
1739    ///     "Your time zone is: UK Time",
1740    /// );
1741    /// ```
1742    #[cfg(feature = "compiled_data")]
1743    pub fn include_time_zone_location_names(&mut self) -> Result<&mut Self, PatternLoadError> {
1744        self.load_time_zone_location_names(&crate::provider::Baked)
1745    }
1746
1747    /// Loads exemplar city names for time zone formatting.
1748    pub fn load_time_zone_exemplar_city_names<P>(
1749        &mut self,
1750        provider: &P,
1751    ) -> Result<&mut Self, PatternLoadError>
1752    where
1753        P: DataProvider<tz::CitiesOverrideV1> + DataProvider<tz::CitiesRootV1> + ?Sized,
1754    {
1755        self.inner.load_time_zone_exemplar_city_names(
1756            &tz::CitiesOverrideV1::bind(provider),
1757            &tz::CitiesRootV1::bind(provider),
1758            self.prefs,
1759        )?;
1760        Ok(self)
1761    }
1762
1763    /// Includes exemplar city names for time zone formatting with compiled data.
1764    ///
1765    /// Important: The `VVV` format requires location data in addition to exemplar
1766    /// city data. Also call either:
1767    ///
1768    /// - [`FixedCalendarDateTimeNames::include_time_zone_location_names`]
1769    /// - [`FixedCalendarDateTimeNames::load_time_zone_location_names`]
1770    ///
1771    /// ✨ *Enabled with the `compiled_data` Cargo feature.*
1772    ///
1773    /// # Examples
1774    ///
1775    /// ```
1776    /// use icu::calendar::Gregorian;
1777    /// use icu::datetime::fieldsets::enums::ZoneFieldSet;
1778    /// use icu::datetime::input::ZonedDateTime;
1779    /// use icu::datetime::pattern::DateTimePattern;
1780    /// use icu::datetime::pattern::FixedCalendarDateTimeNames;
1781    /// use icu::locale::locale;
1782    /// use icu::time::zone::IanaParser;
1783    /// use writeable::assert_try_writeable_eq;
1784    ///
1785    /// let mut zone_london_winter = ZonedDateTime::try_strict_from_str(
1786    ///     "2024-01-01T00:00:00+00:00[Europe/London]",
1787    ///     Gregorian,
1788    ///     IanaParser::new(),
1789    /// )
1790    /// .unwrap()
1791    /// .zone;
1792    ///
1793    /// let mut names =
1794    ///     FixedCalendarDateTimeNames::<Gregorian, ZoneFieldSet>::try_new(
1795    ///         locale!("en-GB").into(),
1796    ///     )
1797    ///     .unwrap();
1798    ///
1799    /// names.include_time_zone_location_names().unwrap();
1800    /// names.include_time_zone_exemplar_city_names().unwrap();
1801    ///
1802    /// // Try `VVVV`:
1803    /// let pattern_str = "'Your time zone is:' VVV";
1804    /// let pattern: DateTimePattern = pattern_str.parse().unwrap();
1805    ///
1806    /// assert_try_writeable_eq!(
1807    ///     names
1808    ///         .with_pattern_unchecked(&pattern)
1809    ///         .format(&zone_london_winter),
1810    ///     "Your time zone is: London",
1811    /// );
1812    /// ```
1813    #[cfg(feature = "compiled_data")]
1814    pub fn include_time_zone_exemplar_city_names(&mut self) -> Result<&mut Self, PatternLoadError> {
1815        self.load_time_zone_exemplar_city_names(&crate::provider::Baked)
1816    }
1817
1818    /// Loads generic non-location long time zone names.
1819    // NOTE: If a buffer version of this fn is added in 2.x, it should use the CompatProvider
1820    pub fn load_time_zone_generic_long_names<P>(
1821        &mut self,
1822        provider: &P,
1823    ) -> Result<&mut Self, PatternLoadError>
1824    where
1825        P: DataProvider<tz::MzGenericLongV1>
1826            + DataProvider<tz::MzStandardLongV1>
1827            + DataProvider<tz::MzPeriodV1>
1828            + ?Sized,
1829    {
1830        self.inner.load_time_zone_generic_long_names(
1831            &tz::MzGenericLongV1::bind(provider),
1832            &tz::MzStandardLongV1::bind(provider),
1833            &tz::MzPeriodV1::bind(provider),
1834            self.prefs,
1835            &mut self.metadata,
1836        )?;
1837        Ok(self)
1838    }
1839
1840    /// Includes generic non-location long time zone names with compiled data.
1841    ///
1842    /// Important: When performing manual time zone data loading, in addition to the
1843    /// specific time zone format data, also call either:
1844    ///
1845    /// - [`FixedCalendarDateTimeNames::include_time_zone_essentials`]
1846    /// - [`FixedCalendarDateTimeNames::load_time_zone_essentials`]
1847    ///
1848    /// ✨ *Enabled with the `compiled_data` Cargo feature.*
1849    ///
1850    /// # Examples
1851    ///
1852    /// ```
1853    /// use icu::calendar::Gregorian;
1854    /// use icu::datetime::fieldsets::enums::ZoneFieldSet;
1855    /// use icu::datetime::input::ZonedDateTime;
1856    /// use icu::datetime::pattern::DateTimePattern;
1857    /// use icu::datetime::pattern::FixedCalendarDateTimeNames;
1858    /// use icu::locale::locale;
1859    /// use icu::time::zone::IanaParser;
1860    /// use writeable::assert_try_writeable_eq;
1861    ///
1862    /// let mut zone_berlin_winter = ZonedDateTime::try_strict_from_str(
1863    ///     "2024-01-01T00:00:00+01:00[Europe/Berlin]",
1864    ///     Gregorian,
1865    ///     IanaParser::new(),
1866    /// )
1867    /// .unwrap()
1868    /// .zone;
1869    /// let mut zone_berlin_summer = ZonedDateTime::try_strict_from_str(
1870    ///     "2024-07-01T00:00:00+02:00[Europe/Berlin]",
1871    ///     Gregorian,
1872    ///     IanaParser::new(),
1873    /// )
1874    /// .unwrap()
1875    /// .zone;
1876    ///
1877    /// let mut names =
1878    ///     FixedCalendarDateTimeNames::<Gregorian, ZoneFieldSet>::try_new(
1879    ///         locale!("en-GB").into(),
1880    ///     )
1881    ///     .unwrap();
1882    ///
1883    /// names.include_time_zone_essentials().unwrap();
1884    /// names.include_time_zone_generic_long_names().unwrap();
1885    /// names.include_time_zone_location_names().unwrap();
1886    ///
1887    /// // Create a pattern with symbol `vvvv`:
1888    /// let pattern_str = "'Your time zone is:' vvvv";
1889    /// let pattern: DateTimePattern = pattern_str.parse().unwrap();
1890    ///
1891    /// assert_try_writeable_eq!(
1892    ///     names
1893    ///         .with_pattern_unchecked(&pattern)
1894    ///         .format(&zone_berlin_winter),
1895    ///     "Your time zone is: Central European Time",
1896    /// );
1897    /// assert_try_writeable_eq!(
1898    ///     names
1899    ///         .with_pattern_unchecked(&pattern)
1900    ///         .format(&zone_berlin_summer),
1901    ///     "Your time zone is: Central European Time",
1902    /// );
1903    /// ```
1904    #[cfg(feature = "compiled_data")]
1905    pub fn include_time_zone_generic_long_names(&mut self) -> Result<&mut Self, PatternLoadError> {
1906        self.load_time_zone_generic_long_names(&crate::provider::Baked)
1907    }
1908
1909    /// Loads generic non-location short time zone names.
1910    // NOTE: If a buffer version of this fn is added in 2.x, it should use the CompatProvider
1911    pub fn load_time_zone_generic_short_names<P>(
1912        &mut self,
1913        provider: &P,
1914    ) -> Result<&mut Self, PatternLoadError>
1915    where
1916        P: DataProvider<tz::MzGenericShortV1> + DataProvider<tz::MzPeriodV1> + ?Sized,
1917    {
1918        self.inner.load_time_zone_generic_short_names(
1919            &tz::MzGenericShortV1::bind(provider),
1920            &tz::MzPeriodV1::bind(provider),
1921            self.prefs,
1922            &mut self.metadata,
1923        )?;
1924        Ok(self)
1925    }
1926
1927    /// Includes generic non-location short time zone names with compiled data.
1928    ///
1929    /// Important: When performing manual time zone data loading, in addition to the
1930    /// specific time zone format data, also call either:
1931    ///
1932    /// - [`FixedCalendarDateTimeNames::include_time_zone_essentials`]
1933    /// - [`FixedCalendarDateTimeNames::load_time_zone_essentials`]
1934    ///
1935    /// ✨ *Enabled with the `compiled_data` Cargo feature.*
1936    ///
1937    /// # Examples
1938    ///
1939    /// ```
1940    /// use icu::calendar::Gregorian;
1941    /// use icu::datetime::fieldsets::enums::ZoneFieldSet;
1942    /// use icu::datetime::input::ZonedDateTime;
1943    /// use icu::datetime::pattern::DateTimePattern;
1944    /// use icu::datetime::pattern::FixedCalendarDateTimeNames;
1945    /// use icu::locale::locale;
1946    /// use icu::time::zone::IanaParser;
1947    /// use writeable::assert_try_writeable_eq;
1948    ///
1949    /// let mut zone_berlin_winter = ZonedDateTime::try_strict_from_str(
1950    ///     "2024-01-01T00:00:00+01:00[Europe/Berlin]",
1951    ///     Gregorian,
1952    ///     IanaParser::new(),
1953    /// )
1954    /// .unwrap()
1955    /// .zone;
1956    /// let mut zone_berlin_summer = ZonedDateTime::try_strict_from_str(
1957    ///     "2024-07-01T00:00:00+02:00[Europe/Berlin]",
1958    ///     Gregorian,
1959    ///     IanaParser::new(),
1960    /// )
1961    /// .unwrap()
1962    /// .zone;
1963    ///
1964    /// let mut names =
1965    ///     FixedCalendarDateTimeNames::<Gregorian, ZoneFieldSet>::try_new(
1966    ///         locale!("en-GB").into(),
1967    ///     )
1968    ///     .unwrap();
1969    ///
1970    /// names.include_time_zone_essentials().unwrap();
1971    /// names.include_time_zone_generic_short_names().unwrap();
1972    /// names.include_time_zone_location_names().unwrap();
1973    ///
1974    /// // Create a pattern with symbol `v`:
1975    /// let pattern_str = "'Your time zone is:' v";
1976    /// let pattern: DateTimePattern = pattern_str.parse().unwrap();
1977    ///
1978    /// assert_try_writeable_eq!(
1979    ///     names
1980    ///         .with_pattern_unchecked(&pattern)
1981    ///         .format(&zone_berlin_winter),
1982    ///     "Your time zone is: CET",
1983    /// );
1984    /// assert_try_writeable_eq!(
1985    ///     names
1986    ///         .with_pattern_unchecked(&pattern)
1987    ///         .format(&zone_berlin_summer),
1988    ///     "Your time zone is: CET",
1989    /// );
1990    /// ```
1991    #[cfg(feature = "compiled_data")]
1992    pub fn include_time_zone_generic_short_names(&mut self) -> Result<&mut Self, PatternLoadError> {
1993        self.load_time_zone_generic_short_names(&crate::provider::Baked)
1994    }
1995
1996    /// Loads specific non-location long time zone names.
1997    // NOTE: If a buffer version of this fn is added in 2.x, it should use the CompatProvider
1998    pub fn load_time_zone_specific_long_names<P>(
1999        &mut self,
2000        provider: &P,
2001    ) -> Result<&mut Self, PatternLoadError>
2002    where
2003        P: DataProvider<tz::MzSpecificLongV1>
2004            + DataProvider<tz::MzStandardLongV1>
2005            + DataProvider<tz::MzPeriodV1>
2006            + ?Sized,
2007    {
2008        self.inner.load_time_zone_specific_long_names(
2009            &tz::MzSpecificLongV1::bind(provider),
2010            &tz::MzStandardLongV1::bind(provider),
2011            &tz::MzPeriodV1::bind(provider),
2012            self.prefs,
2013            &mut self.metadata,
2014        )?;
2015        Ok(self)
2016    }
2017
2018    /// Includes specific non-location long time zone names with compiled data.
2019    ///
2020    /// Important: When performing manual time zone data loading, in addition to the
2021    /// specific time zone format data, also call either:
2022    ///
2023    /// - [`FixedCalendarDateTimeNames::include_time_zone_essentials`]
2024    /// - [`FixedCalendarDateTimeNames::load_time_zone_essentials`]
2025    ///
2026    /// ✨ *Enabled with the `compiled_data` Cargo feature.*
2027    ///
2028    /// # Examples
2029    ///
2030    /// ```
2031    /// use icu::calendar::Gregorian;
2032    /// use icu::datetime::fieldsets::enums::ZoneFieldSet;
2033    /// use icu::datetime::input::ZonedDateTime;
2034    /// use icu::datetime::pattern::DateTimePattern;
2035    /// use icu::datetime::pattern::FixedCalendarDateTimeNames;
2036    /// use icu::locale::locale;
2037    /// use icu::time::zone::IanaParser;
2038    /// use writeable::assert_try_writeable_eq;
2039    ///
2040    /// let mut zone_london_winter = ZonedDateTime::try_strict_from_str(
2041    ///     "2024-01-01T00:00:00+00:00[Europe/London]",
2042    ///     Gregorian,
2043    ///     IanaParser::new(),
2044    /// )
2045    /// .unwrap()
2046    /// .zone;
2047    /// let mut zone_london_summer = ZonedDateTime::try_strict_from_str(
2048    ///     "2024-07-01T00:00:00+01:00[Europe/London]",
2049    ///     Gregorian,
2050    ///     IanaParser::new(),
2051    /// )
2052    /// .unwrap()
2053    /// .zone;
2054    ///
2055    /// let mut names =
2056    ///     FixedCalendarDateTimeNames::<Gregorian, ZoneFieldSet>::try_new(
2057    ///         locale!("en-GB").into(),
2058    ///     )
2059    ///     .unwrap();
2060    ///
2061    /// names.include_time_zone_essentials().unwrap();
2062    /// names.include_time_zone_specific_long_names().unwrap();
2063    ///
2064    /// // Create a pattern with symbol `zzzz`:
2065    /// let pattern_str = "'Your time zone is:' zzzz";
2066    /// let pattern: DateTimePattern = pattern_str.parse().unwrap();
2067    ///
2068    /// assert_try_writeable_eq!(
2069    ///     names
2070    ///         .with_pattern_unchecked(&pattern)
2071    ///         .format(&zone_london_winter),
2072    ///     "Your time zone is: Greenwich Mean Time",
2073    /// );
2074    /// assert_try_writeable_eq!(
2075    ///     names
2076    ///         .with_pattern_unchecked(&pattern)
2077    ///         .format(&zone_london_summer),
2078    ///     "Your time zone is: British Summer Time",
2079    /// );
2080    /// ```
2081    #[cfg(feature = "compiled_data")]
2082    pub fn include_time_zone_specific_long_names(&mut self) -> Result<&mut Self, PatternLoadError> {
2083        self.load_time_zone_specific_long_names(&crate::provider::Baked)
2084    }
2085
2086    /// Loads specific non-location short time zone names.
2087    // NOTE: If a buffer version of this fn is added in 2.x, it should use the CompatProvider
2088    pub fn load_time_zone_specific_short_names<P>(
2089        &mut self,
2090        provider: &P,
2091    ) -> Result<&mut Self, PatternLoadError>
2092    where
2093        P: DataProvider<tz::MzSpecificShortV1> + DataProvider<tz::MzPeriodV1> + ?Sized,
2094    {
2095        self.inner.load_time_zone_specific_short_names(
2096            &tz::MzSpecificShortV1::bind(provider),
2097            &tz::MzPeriodV1::bind(provider),
2098            self.prefs,
2099            &mut self.metadata,
2100        )?;
2101        Ok(self)
2102    }
2103
2104    /// Includes specific non-location short time zone names with compiled data.
2105    ///
2106    /// Important: When performing manual time zone data loading, in addition to the
2107    /// specific time zone format data, also call either:
2108    ///
2109    /// - [`FixedCalendarDateTimeNames::include_time_zone_essentials`]
2110    /// - [`FixedCalendarDateTimeNames::load_time_zone_essentials`]
2111    ///
2112    /// ✨ *Enabled with the `compiled_data` Cargo feature.*
2113    ///
2114    /// # Examples
2115    ///
2116    /// ```
2117    /// use icu::calendar::Gregorian;
2118    /// use icu::datetime::fieldsets::enums::ZoneFieldSet;
2119    /// use icu::datetime::input::ZonedDateTime;
2120    /// use icu::datetime::pattern::DateTimePattern;
2121    /// use icu::datetime::pattern::FixedCalendarDateTimeNames;
2122    /// use icu::locale::locale;
2123    /// use icu::time::zone::IanaParser;
2124    /// use writeable::assert_try_writeable_eq;
2125    ///
2126    /// let mut zone_london_winter = ZonedDateTime::try_strict_from_str(
2127    ///     "2024-01-01T00:00:00+00:00[Europe/London]",
2128    ///     Gregorian,
2129    ///     IanaParser::new(),
2130    /// )
2131    /// .unwrap()
2132    /// .zone;
2133    /// let mut zone_london_summer = ZonedDateTime::try_strict_from_str(
2134    ///     "2024-07-01T00:00:00+01:00[Europe/London]",
2135    ///     Gregorian,
2136    ///     IanaParser::new(),
2137    /// )
2138    /// .unwrap()
2139    /// .zone;
2140    ///
2141    /// let mut names =
2142    ///     FixedCalendarDateTimeNames::<Gregorian, ZoneFieldSet>::try_new(
2143    ///         locale!("en-GB").into(),
2144    ///     )
2145    ///     .unwrap();
2146    ///
2147    /// names.include_time_zone_essentials().unwrap();
2148    /// names.include_time_zone_specific_short_names().unwrap();
2149    ///
2150    /// // Create a pattern with symbol `z`:
2151    /// let pattern_str = "'Your time zone is:' z";
2152    /// let pattern: DateTimePattern = pattern_str.parse().unwrap();
2153    ///
2154    /// assert_try_writeable_eq!(
2155    ///     names
2156    ///         .with_pattern_unchecked(&pattern)
2157    ///         .format(&zone_london_winter),
2158    ///     "Your time zone is: GMT",
2159    /// );
2160    /// assert_try_writeable_eq!(
2161    ///     names
2162    ///         .with_pattern_unchecked(&pattern)
2163    ///         .format(&zone_london_summer),
2164    ///     "Your time zone is: BST",
2165    /// );
2166    /// ```
2167    #[cfg(feature = "compiled_data")]
2168    pub fn include_time_zone_specific_short_names(
2169        &mut self,
2170    ) -> Result<&mut Self, PatternLoadError> {
2171        self.load_time_zone_specific_short_names(&crate::provider::Baked)
2172    }
2173
2174    /// Loads generic non-location short time zone names
2175    /// and all data required for its fallback formats.
2176    ///
2177    /// See [`GenericShort`](crate::fieldsets::zone::GenericShort)
2178    // NOTE: If a buffer version of this fn is added in 2.x, it should use the CompatProvider
2179    pub fn load_time_zone_generic_short_names_with_fallback<P>(
2180        &mut self,
2181        provider: &P,
2182    ) -> Result<&mut Self, PatternLoadError>
2183    where
2184        P: DataProvider<DecimalSymbolsV1>
2185            + DataProvider<DecimalDigitsV1>
2186            + DataProvider<tz::EssentialsV1>
2187            + DataProvider<tz::LocationsOverrideV1>
2188            + DataProvider<tz::LocationsRootV1>
2189            + DataProvider<tz::MzGenericShortV1>
2190            + DataProvider<tz::MzPeriodV1>
2191            + ?Sized,
2192    {
2193        let error_field = self.inner.load_time_zone_field_v_except_decimals(
2194            &tz::EssentialsV1::bind(provider),
2195            &tz::LocationsOverrideV1::bind(provider),
2196            &tz::LocationsRootV1::bind(provider),
2197            &tz::MzGenericShortV1::bind(provider),
2198            &tz::MzPeriodV1::bind(provider),
2199            self.prefs,
2200            &mut self.metadata,
2201        )?;
2202        self.load_decimal_formatter(provider)
2203            .map_err(|e| PatternLoadError::Data(e, error_field))?;
2204        Ok(self)
2205    }
2206
2207    /// Includes generic non-location short time zone names
2208    /// and all data required for its fallback formats, with compiled data.
2209    ///
2210    /// See [`GenericShort`](crate::fieldsets::zone::GenericShort)
2211    ///
2212    /// ✨ *Enabled with the `compiled_data` Cargo feature.*
2213    #[cfg(feature = "compiled_data")]
2214    pub fn include_time_zone_generic_short_names_with_fallback(
2215        &mut self,
2216    ) -> Result<&mut Self, PatternLoadError> {
2217        let error_field = self.inner.load_time_zone_field_v_except_decimals(
2218            &tz::EssentialsV1::bind(&crate::provider::Baked),
2219            &tz::LocationsOverrideV1::bind(&crate::provider::Baked),
2220            &tz::LocationsRootV1::bind(&crate::provider::Baked),
2221            &tz::MzGenericShortV1::bind(&crate::provider::Baked),
2222            &tz::MzPeriodV1::bind(&crate::provider::Baked),
2223            self.prefs,
2224            &mut self.metadata,
2225        )?;
2226        self.include_decimal_formatter()
2227            .map_err(|e| PatternLoadError::Data(e, error_field))?;
2228        Ok(self)
2229    }
2230
2231    /// Loads generic non-location long time zone names
2232    /// and all data required for its fallback formats.
2233    ///
2234    /// See [`GenericLong`](crate::fieldsets::zone::GenericLong)
2235    // NOTE: If a buffer version of this fn is added in 2.x, it should use the CompatProvider
2236    pub fn load_time_zone_generic_long_names_with_fallback<P>(
2237        &mut self,
2238        provider: &P,
2239    ) -> Result<&mut Self, PatternLoadError>
2240    where
2241        P: DataProvider<DecimalSymbolsV1>
2242            + DataProvider<DecimalDigitsV1>
2243            + DataProvider<tz::EssentialsV1>
2244            + DataProvider<tz::LocationsOverrideV1>
2245            + DataProvider<tz::LocationsRootV1>
2246            + DataProvider<tz::MzGenericLongV1>
2247            + DataProvider<tz::MzStandardLongV1>
2248            + DataProvider<tz::MzPeriodV1>
2249            + ?Sized,
2250    {
2251        let error_field = self.inner.load_time_zone_field_vvvv_except_decimals(
2252            &tz::EssentialsV1::bind(provider),
2253            &tz::LocationsOverrideV1::bind(provider),
2254            &tz::LocationsRootV1::bind(provider),
2255            &tz::MzGenericLongV1::bind(provider),
2256            &tz::MzStandardLongV1::bind(provider),
2257            &tz::MzPeriodV1::bind(provider),
2258            self.prefs,
2259            &mut self.metadata,
2260        )?;
2261        self.load_decimal_formatter(provider)
2262            .map_err(|e| PatternLoadError::Data(e, error_field))?;
2263        Ok(self)
2264    }
2265
2266    /// Includes generic non-location long time zone names
2267    /// and all data required for its fallback formats, with compiled data.
2268    ///
2269    /// See [`GenericLong`](crate::fieldsets::zone::GenericLong)
2270    ///
2271    /// ✨ *Enabled with the `compiled_data` Cargo feature.*
2272    #[cfg(feature = "compiled_data")]
2273    pub fn include_time_zone_generic_long_names_with_fallback(
2274        &mut self,
2275    ) -> Result<&mut Self, PatternLoadError> {
2276        let error_field = self.inner.load_time_zone_field_vvvv_except_decimals(
2277            &tz::EssentialsV1::bind(&crate::provider::Baked),
2278            &tz::LocationsOverrideV1::bind(&crate::provider::Baked),
2279            &tz::LocationsRootV1::bind(&crate::provider::Baked),
2280            &tz::MzGenericLongV1::bind(&crate::provider::Baked),
2281            &tz::MzStandardLongV1::bind(&crate::provider::Baked),
2282            &tz::MzPeriodV1::bind(&crate::provider::Baked),
2283            self.prefs,
2284            &mut self.metadata,
2285        )?;
2286        self.include_decimal_formatter()
2287            .map_err(|e| PatternLoadError::Data(e, error_field))?;
2288        Ok(self)
2289    }
2290
2291    /// Loads specific non-location short time zone names
2292    /// and all data required for its fallback formats
2293    /// except for decimal formatting.
2294    ///
2295    /// See [`SpecificShort`](crate::fieldsets::zone::SpecificShort)
2296    // NOTE: If a buffer version of this fn is added in 2.x, it should use the CompatProvider
2297    pub fn load_time_zone_specific_short_names_with_fallback<P>(
2298        &mut self,
2299        provider: &P,
2300    ) -> Result<&mut Self, PatternLoadError>
2301    where
2302        P: DataProvider<DecimalSymbolsV1>
2303            + DataProvider<DecimalDigitsV1>
2304            + DataProvider<tz::EssentialsV1>
2305            + DataProvider<tz::LocationsOverrideV1>
2306            + DataProvider<tz::LocationsRootV1>
2307            + DataProvider<tz::MzSpecificShortV1>
2308            + DataProvider<tz::MzPeriodV1>
2309            + ?Sized,
2310    {
2311        let error_field = self.inner.load_time_zone_field_z_except_decimals(
2312            &tz::EssentialsV1::bind(provider),
2313            &tz::MzSpecificShortV1::bind(provider),
2314            &tz::MzPeriodV1::bind(provider),
2315            self.prefs,
2316            &mut self.metadata,
2317        )?;
2318        self.load_decimal_formatter(provider)
2319            .map_err(|e| PatternLoadError::Data(e, error_field))?;
2320        Ok(self)
2321    }
2322
2323    /// Includes specific non-location short time zone names
2324    /// and all data required for its fallback formats
2325    /// except for decimal formatting, with compiled data.
2326    ///
2327    /// See [`SpecificShort`](crate::fieldsets::zone::SpecificShort)
2328    ///
2329    /// ✨ *Enabled with the `compiled_data` Cargo feature.*
2330    #[cfg(feature = "compiled_data")]
2331    pub fn include_time_zone_specific_short_names_with_fallback(
2332        &mut self,
2333    ) -> Result<&mut Self, PatternLoadError> {
2334        let error_field = self.inner.load_time_zone_field_z_except_decimals(
2335            &tz::EssentialsV1::bind(&crate::provider::Baked),
2336            &tz::MzSpecificShortV1::bind(&crate::provider::Baked),
2337            &tz::MzPeriodV1::bind(&crate::provider::Baked),
2338            self.prefs,
2339            &mut self.metadata,
2340        )?;
2341        self.include_decimal_formatter()
2342            .map_err(|e| PatternLoadError::Data(e, error_field))?;
2343        Ok(self)
2344    }
2345
2346    /// Loads specific non-location long time zone names
2347    /// and all data required for its fallback formats
2348    /// except for decimal formatting.
2349    ///
2350    /// See [`SpecificLong`](crate::fieldsets::zone::SpecificLong)
2351    // NOTE: If a buffer version of this fn is added in 2.x, it should use the CompatProvider
2352    pub fn load_time_zone_specific_long_names_with_fallback<P>(
2353        &mut self,
2354        provider: &P,
2355    ) -> Result<&mut Self, PatternLoadError>
2356    where
2357        P: DataProvider<DecimalSymbolsV1>
2358            + DataProvider<DecimalDigitsV1>
2359            + DataProvider<tz::EssentialsV1>
2360            + DataProvider<tz::LocationsOverrideV1>
2361            + DataProvider<tz::LocationsRootV1>
2362            + DataProvider<tz::MzSpecificLongV1>
2363            + DataProvider<tz::MzStandardLongV1>
2364            + DataProvider<tz::MzPeriodV1>
2365            + ?Sized,
2366    {
2367        let error_field = self.inner.load_time_zone_field_zzzz_except_decimals(
2368            &tz::EssentialsV1::bind(provider),
2369            &tz::LocationsOverrideV1::bind(provider),
2370            &tz::LocationsRootV1::bind(provider),
2371            &tz::MzStandardLongV1::bind(provider),
2372            &tz::MzSpecificLongV1::bind(provider),
2373            &tz::MzPeriodV1::bind(provider),
2374            self.prefs,
2375            &mut self.metadata,
2376        )?;
2377        self.load_decimal_formatter(provider)
2378            .map_err(|e| PatternLoadError::Data(e, error_field))?;
2379        Ok(self)
2380    }
2381
2382    /// Includes specific non-location long time zone names
2383    /// and all data required for its fallback formats
2384    /// except for decimal formatting, with compiled data.
2385    ///
2386    /// See [`SpecificLong`](crate::fieldsets::zone::SpecificLong)
2387    ///
2388    /// ✨ *Enabled with the `compiled_data` Cargo feature.*
2389    #[cfg(feature = "compiled_data")]
2390    pub fn include_time_zone_specific_long_names_with_fallback(
2391        &mut self,
2392    ) -> Result<&mut Self, PatternLoadError> {
2393        let error_field = self.inner.load_time_zone_field_zzzz_except_decimals(
2394            &tz::EssentialsV1::bind(&crate::provider::Baked),
2395            &tz::LocationsOverrideV1::bind(&crate::provider::Baked),
2396            &tz::LocationsRootV1::bind(&crate::provider::Baked),
2397            &tz::MzStandardLongV1::bind(&crate::provider::Baked),
2398            &tz::MzSpecificLongV1::bind(&crate::provider::Baked),
2399            &tz::MzPeriodV1::bind(&crate::provider::Baked),
2400            self.prefs,
2401            &mut self.metadata,
2402        )?;
2403        self.include_decimal_formatter()
2404            .map_err(|e| PatternLoadError::Data(e, error_field))?;
2405        Ok(self)
2406    }
2407
2408    /// Loads all data for short and long localized offset time zone formatting
2409    /// except for decimal formatting.
2410    ///
2411    /// See:
2412    ///
2413    /// - [`LocalizedOffsetShort`](crate::fieldsets::zone::LocalizedOffsetShort)
2414    /// - [`LocalizedOffsetLong`](crate::fieldsets::zone::LocalizedOffsetLong)
2415    pub fn load_time_zone_localized_offset_names_with_fallback<P>(
2416        &mut self,
2417        provider: &P,
2418    ) -> Result<&mut Self, PatternLoadError>
2419    where
2420        P: DataProvider<DecimalSymbolsV1>
2421            + DataProvider<DecimalDigitsV1>
2422            + DataProvider<tz::EssentialsV1>
2423            + ?Sized,
2424    {
2425        let error_field = self.inner.load_time_zone_field_O_except_decimals(
2426            &tz::EssentialsV1::bind(provider),
2427            self.prefs,
2428        )?;
2429        self.load_decimal_formatter(provider)
2430            .map_err(|e| PatternLoadError::Data(e, error_field))?;
2431        Ok(self)
2432    }
2433
2434    /// Includes all data for short and long localized offset time zone formatting
2435    /// except for decimal formatting, with compiled data.
2436    ///
2437    /// See:
2438    ///
2439    /// - [`LocalizedOffsetShort`](crate::fieldsets::zone::LocalizedOffsetShort)
2440    /// - [`LocalizedOffsetLong`](crate::fieldsets::zone::LocalizedOffsetLong)
2441    ///
2442    /// ✨ *Enabled with the `compiled_data` Cargo feature.*
2443    #[cfg(feature = "compiled_data")]
2444    pub fn include_time_zone_localized_offset_names_with_fallback(
2445        &mut self,
2446    ) -> Result<&mut Self, PatternLoadError> {
2447        let error_field = self.inner.load_time_zone_field_O_except_decimals(
2448            &tz::EssentialsV1::bind(&crate::provider::Baked),
2449            self.prefs,
2450        )?;
2451        self.include_decimal_formatter()
2452            .map_err(|e| PatternLoadError::Data(e, error_field))?;
2453        Ok(self)
2454    }
2455
2456    /// Loads a [`DecimalFormatter`] from a data provider.
2457    #[inline]
2458    pub fn load_decimal_formatter<P>(&mut self, provider: &P) -> Result<&mut Self, DataError>
2459    where
2460        P: DataProvider<DecimalSymbolsV1> + DataProvider<DecimalDigitsV1> + ?Sized,
2461    {
2462        self.inner
2463            .load_decimal_formatter(&ExternalLoaderUnstable(provider), self.prefs)?;
2464        Ok(self)
2465    }
2466
2467    /// Loads a [`DecimalFormatter`] with compiled data.
2468    ///
2469    /// ✨ *Enabled with the `compiled_data` Cargo feature.*
2470    ///
2471    /// # Examples
2472    ///
2473    /// ```
2474    /// use icu::datetime::fieldsets::enums::TimeFieldSet;
2475    /// use icu::datetime::input::Time;
2476    /// use icu::datetime::pattern::DateTimePattern;
2477    /// use icu::datetime::pattern::FixedCalendarDateTimeNames;
2478    /// use icu::locale::locale;
2479    /// use writeable::assert_try_writeable_eq;
2480    ///
2481    /// let mut names = FixedCalendarDateTimeNames::<(), TimeFieldSet>::try_new(
2482    ///     locale!("bn").into(),
2483    /// )
2484    /// .unwrap();
2485    /// names.include_decimal_formatter();
2486    ///
2487    /// // Create a pattern for the time, which is all numbers
2488    /// let pattern_str = "'The current 24-hour time is:' HH:mm";
2489    /// let pattern: DateTimePattern = pattern_str.parse().unwrap();
2490    ///
2491    /// let time = Time::try_new(6, 40, 33, 0).unwrap();
2492    ///
2493    /// assert_try_writeable_eq!(
2494    ///     names.with_pattern_unchecked(&pattern).format(&time),
2495    ///     "The current 24-hour time is: ০৬:৪০",
2496    /// );
2497    /// ```
2498    #[cfg(feature = "compiled_data")]
2499    #[inline]
2500    pub fn include_decimal_formatter(&mut self) -> Result<&mut Self, DataError> {
2501        self.inner
2502            .load_decimal_formatter(&ExternalLoaderCompiledData, self.prefs)?;
2503        Ok(self)
2504    }
2505}
2506
2507impl<C: CldrCalendar, FSet: DateTimeNamesMarker> FixedCalendarDateTimeNames<C, FSet> {
2508    /// Associates this [`FixedCalendarDateTimeNames`] with a pattern
2509    /// without checking that all necessary data is loaded.
2510    ///
2511    /// Use this function if you know at compile time what fields your pattern contains.
2512    #[inline]
2513    pub fn with_pattern_unchecked<'l>(
2514        &'l self,
2515        pattern: &'l DateTimePattern,
2516    ) -> DateTimePatternFormatter<'l, C, FSet> {
2517        DateTimePatternFormatter::new(pattern.as_borrowed(), self.inner.as_borrowed())
2518    }
2519
2520    /// Associates this [`FixedCalendarDateTimeNames`] with a datetime pattern
2521    /// and loads all data required for that pattern.
2522    ///
2523    /// Does not duplicate textual field symbols. See [#4337](https://github.com/unicode-org/icu4x/issues/4337)
2524    // NOTE: If a buffer version of this fn is added in 2.x, it should use the CompatProvider
2525    pub fn load_for_pattern<'l, P>(
2526        &'l mut self,
2527        provider: &P,
2528        pattern: &'l DateTimePattern,
2529    ) -> Result<DateTimePatternFormatter<'l, C, FSet>, PatternLoadError>
2530    where
2531        P: DataProvider<C::YearNamesV1>
2532            + DataProvider<C::MonthNamesV1>
2533            + DataProvider<WeekdayNamesV1>
2534            + DataProvider<DayPeriodNamesV1>
2535            + DataProvider<tz::EssentialsV1>
2536            + DataProvider<tz::LocationsOverrideV1>
2537            + DataProvider<tz::LocationsRootV1>
2538            + DataProvider<tz::CitiesOverrideV1>
2539            + DataProvider<tz::CitiesRootV1>
2540            + DataProvider<tz::MzGenericLongV1>
2541            + DataProvider<tz::MzGenericShortV1>
2542            + DataProvider<tz::MzStandardLongV1>
2543            + DataProvider<tz::MzSpecificLongV1>
2544            + DataProvider<tz::MzSpecificShortV1>
2545            + DataProvider<tz::MzPeriodV1>
2546            + DataProvider<DecimalSymbolsV1>
2547            + DataProvider<DecimalDigitsV1>
2548            + ?Sized,
2549    {
2550        let locale = self.prefs;
2551        self.inner.load_for_pattern(
2552            &C::YearNamesV1::bind(provider),
2553            &C::MonthNamesV1::bind(provider),
2554            &WeekdayNamesV1::bind(provider),
2555            &DayPeriodNamesV1::bind(provider),
2556            &tz::EssentialsV1::bind(provider),
2557            &tz::LocationsRootV1::bind(provider),
2558            &tz::LocationsOverrideV1::bind(provider),
2559            &tz::CitiesRootV1::bind(provider),
2560            &tz::CitiesOverrideV1::bind(provider),
2561            &tz::MzGenericLongV1::bind(provider),
2562            &tz::MzGenericShortV1::bind(provider),
2563            &tz::MzStandardLongV1::bind(provider),
2564            &tz::MzSpecificLongV1::bind(provider),
2565            &tz::MzSpecificShortV1::bind(provider),
2566            &tz::MzPeriodV1::bind(provider),
2567            &ExternalLoaderUnstable(provider),
2568            locale,
2569            pattern.iter_items(),
2570            &mut self.metadata,
2571        )?;
2572        Ok(DateTimePatternFormatter::new(
2573            pattern.as_borrowed(),
2574            self.inner.as_borrowed(),
2575        ))
2576    }
2577
2578    /// Associates this [`FixedCalendarDateTimeNames`] with a pattern
2579    /// and includes all data required for that pattern, from compiled data.
2580    ///
2581    /// Does not support duplicate textual field symbols. See [#4337](https://github.com/unicode-org/icu4x/issues/4337)
2582    ///
2583    /// ✨ *Enabled with the `compiled_data` Cargo feature.*
2584    ///
2585    /// # Examples
2586    ///
2587    /// ```
2588    /// use icu::calendar::Gregorian;
2589    /// use icu::datetime::input::Date;
2590    /// use icu::datetime::input::{DateTime, Time};
2591    /// use icu::datetime::pattern::DateTimePattern;
2592    /// use icu::datetime::pattern::FixedCalendarDateTimeNames;
2593    /// use icu::locale::locale;
2594    /// use writeable::assert_try_writeable_eq;
2595    ///
2596    /// let mut names =
2597    ///     FixedCalendarDateTimeNames::<Gregorian>::try_new(locale!("en").into())
2598    ///         .unwrap();
2599    ///
2600    /// // Create a pattern from a pattern string:
2601    /// let pattern_str = "MMM d (EEEE) 'of year' y G 'at' h:mm a";
2602    /// let pattern: DateTimePattern = pattern_str.parse().unwrap();
2603    ///
2604    /// // Load data for the pattern and format:
2605    /// let datetime = DateTime {
2606    ///     date: Date::try_new_gregorian(2023, 12, 5).unwrap(),
2607    ///     time: Time::try_new(17, 43, 12, 0).unwrap(),
2608    /// };
2609    /// assert_try_writeable_eq!(
2610    ///     names
2611    ///         .include_for_pattern(&pattern)
2612    ///         .unwrap()
2613    ///         .format(&datetime),
2614    ///     "Dec 5 (Tuesday) of year 2023 AD at 5:43 PM"
2615    /// );
2616    /// ```
2617    #[cfg(feature = "compiled_data")]
2618    pub fn include_for_pattern<'l>(
2619        &'l mut self,
2620        pattern: &'l DateTimePattern,
2621    ) -> Result<DateTimePatternFormatter<'l, C, FSet>, PatternLoadError>
2622    where
2623        crate::provider::Baked: DataProvider<C::YearNamesV1> + DataProvider<C::MonthNamesV1>,
2624        crate::provider::Baked: DataProvider<C::YearNamesV1> + DataProvider<C::MonthNamesV1>,
2625    {
2626        let locale = self.prefs;
2627        self.inner.load_for_pattern(
2628            &C::YearNamesV1::bind(&crate::provider::Baked),
2629            &C::MonthNamesV1::bind(&crate::provider::Baked),
2630            &WeekdayNamesV1::bind(&crate::provider::Baked),
2631            &DayPeriodNamesV1::bind(&crate::provider::Baked),
2632            &tz::EssentialsV1::bind(&crate::provider::Baked),
2633            &tz::LocationsOverrideV1::bind(&crate::provider::Baked),
2634            &tz::LocationsRootV1::bind(&crate::provider::Baked),
2635            &tz::CitiesOverrideV1::bind(&crate::provider::Baked),
2636            &tz::CitiesRootV1::bind(&crate::provider::Baked),
2637            &tz::MzGenericLongV1::bind(&crate::provider::Baked),
2638            &tz::MzGenericShortV1::bind(&crate::provider::Baked),
2639            &tz::MzStandardLongV1::bind(&crate::provider::Baked),
2640            &tz::MzSpecificLongV1::bind(&crate::provider::Baked),
2641            &tz::MzSpecificShortV1::bind(&crate::provider::Baked),
2642            &tz::MzPeriodV1::bind(&crate::provider::Baked),
2643            &ExternalLoaderCompiledData,
2644            locale,
2645            pattern.iter_items(),
2646            &mut self.metadata,
2647        )?;
2648        Ok(DateTimePatternFormatter::new(
2649            pattern.as_borrowed(),
2650            self.inner.as_borrowed(),
2651        ))
2652    }
2653}
2654
2655impl<C, FSet: DateTimeNamesMarker> FixedCalendarDateTimeNames<C, FSet> {
2656    /// Maps a [`FixedCalendarDateTimeNames`] of a specific `FSet` to a more general `FSet`.
2657    ///
2658    /// For example, this can transform a formatter for [`DateFieldSet`] to one for
2659    /// [`CompositeDateTimeFieldSet`].
2660    ///
2661    /// [`DateFieldSet`]: crate::fieldsets::enums::DateFieldSet
2662    /// [`CompositeDateTimeFieldSet`]: crate::fieldsets::enums::CompositeDateTimeFieldSet
2663    ///
2664    /// # Examples
2665    ///
2666    /// ```
2667    /// use icu::calendar::Gregorian;
2668    /// use icu::datetime::fieldsets::enums::{
2669    ///     CompositeDateTimeFieldSet, DateFieldSet,
2670    /// };
2671    /// use icu::datetime::input::Date;
2672    /// use icu::datetime::input::{DateTime, Time};
2673    /// use icu::datetime::pattern::DateTimePattern;
2674    /// use icu::datetime::pattern::FixedCalendarDateTimeNames;
2675    /// use icu::datetime::pattern::MonthNameLength;
2676    /// use icu::locale::locale;
2677    /// use writeable::assert_try_writeable_eq;
2678    ///
2679    /// // Create an instance that can format abbreviated month names:
2680    /// let mut names: FixedCalendarDateTimeNames<Gregorian, DateFieldSet> =
2681    ///     FixedCalendarDateTimeNames::try_new(locale!("uk").into()).unwrap();
2682    /// names
2683    ///     .include_month_names(MonthNameLength::Abbreviated)
2684    ///     .unwrap();
2685    ///
2686    /// // Test it with a pattern:
2687    /// let pattern_str = "MMM d y";
2688    /// let pattern: DateTimePattern = pattern_str.parse().unwrap();
2689    /// let datetime = DateTime {
2690    ///     date: Date::try_new_gregorian(2023, 11, 20).unwrap(),
2691    ///     time: Time::start_of_day(),
2692    /// };
2693    /// assert_try_writeable_eq!(
2694    ///     names.with_pattern_unchecked(&pattern).format(&datetime),
2695    ///     "лист. 20 2023"
2696    /// );
2697    ///
2698    /// // Convert the field set to `CompositeDateTimeFieldSet`:
2699    /// let composite_names = names.cast_into_fset::<CompositeDateTimeFieldSet>();
2700    ///
2701    /// // It should still work:
2702    /// assert_try_writeable_eq!(
2703    ///     composite_names
2704    ///         .with_pattern_unchecked(&pattern)
2705    ///         .format(&datetime),
2706    ///     "лист. 20 2023"
2707    /// );
2708    /// ```
2709    ///
2710    /// Converting into a narrower type is not supported:
2711    ///
2712    /// ```compile_fail,E0277
2713    /// use icu::calendar::Gregorian;
2714    /// use icu::datetime::pattern::FixedCalendarDateTimeNames;
2715    /// use icu::datetime::fieldsets::enums::{DateFieldSet, CompositeDateTimeFieldSet};
2716    ///
2717    /// let composite_names: FixedCalendarDateTimeNames<Gregorian, CompositeDateTimeFieldSet> = todo!();
2718    ///
2719    /// // error[E0277]: the trait bound `(): From<DataPayloadWithVariables<DayPeriodNamesV1, FieldLength>>` is not satisfied
2720    /// let narrow_names = composite_names.cast_into_fset::<DateFieldSet>();
2721    /// ```
2722    pub fn cast_into_fset<FSet2: DateTimeNamesFrom<FSet>>(
2723        self,
2724    ) -> FixedCalendarDateTimeNames<C, FSet2> {
2725        FixedCalendarDateTimeNames {
2726            prefs: self.prefs,
2727            inner: self.inner.cast_into_fset(),
2728            metadata: self.metadata,
2729            _calendar: PhantomData,
2730        }
2731    }
2732}
2733
2734impl<FSet: DateTimeNamesMarker> DateTimeNames<FSet> {
2735    /// Maps a [`FixedCalendarDateTimeNames`] of a specific `FSet` to a more general `FSet`.
2736    ///
2737    /// For example, this can transform a formatter for [`DateFieldSet`] to one for
2738    /// [`CompositeDateTimeFieldSet`].
2739    ///
2740    /// [`DateFieldSet`]: crate::fieldsets::enums::DateFieldSet
2741    /// [`CompositeDateTimeFieldSet`]: crate::fieldsets::enums::CompositeDateTimeFieldSet
2742    pub fn cast_into_fset<FSet2: DateTimeNamesFrom<FSet>>(self) -> DateTimeNames<FSet2> {
2743        DateTimeNames {
2744            inner: self.inner.cast_into_fset(),
2745            calendar: self.calendar,
2746        }
2747    }
2748}
2749
2750impl<FSet: DateTimeNamesMarker> RawDateTimeNames<FSet> {
2751    pub(crate) fn new_without_number_formatting() -> Self {
2752        Self {
2753            year_names: <FSet::YearNames as NamesContainer<
2754                YearNamesV1,
2755                YearNameLength,
2756            >>::Container::new_empty(),
2757            month_names: <FSet::MonthNames as NamesContainer<
2758                MonthNamesV1,
2759                MonthNameLength,
2760            >>::Container::new_empty(),
2761            weekday_names: <FSet::WeekdayNames as NamesContainer<
2762                WeekdayNamesV1,
2763                WeekdayNameLength,
2764            >>::Container::new_empty(),
2765            dayperiod_names: <FSet::DayPeriodNames as NamesContainer<
2766                DayPeriodNamesV1,
2767                DayPeriodNameLength,
2768            >>::Container::new_empty(),
2769            zone_essentials: <FSet::ZoneEssentials as NamesContainer<
2770                tz::EssentialsV1,
2771                (),
2772            >>::Container::new_empty(),
2773            locations_root: <FSet::ZoneLocationsRoot as NamesContainer<
2774                tz::LocationsRootV1,
2775                (),
2776            >>::Container::new_empty(),
2777            locations: <FSet::ZoneLocations as NamesContainer<
2778                tz::LocationsOverrideV1,
2779                (),
2780            >>::Container::new_empty(),
2781            exemplars: <FSet::ZoneExemplars as NamesContainer<
2782                tz::CitiesOverrideV1,
2783                (),
2784            >>::Container::new_empty(),
2785            exemplars_root: <FSet::ZoneExemplarsRoot as NamesContainer<
2786                tz::CitiesRootV1,
2787                (),
2788            >>::Container::new_empty(),
2789            mz_generic_long: <FSet::ZoneGenericLong as NamesContainer<
2790                tz::MzGenericLongV1,
2791                (),
2792            >>::Container::new_empty(),
2793            mz_generic_short: <FSet::ZoneGenericShort as NamesContainer<
2794                tz::MzGenericShortV1,
2795                (),
2796            >>::Container::new_empty(),
2797            mz_standard_long: <FSet::ZoneStandardLong as NamesContainer<
2798                tz::MzStandardLongV1,
2799                (),
2800            >>::Container::new_empty(),
2801            mz_specific_long: <FSet::ZoneSpecificLong as NamesContainer<
2802                tz::MzSpecificLongV1,
2803                (),
2804            >>::Container::new_empty(),
2805            mz_specific_short: <FSet::ZoneSpecificShort as NamesContainer<
2806                tz::MzSpecificShortV1,
2807                (),
2808            >>::Container::new_empty(),
2809            mz_periods: <FSet::MetazoneLookup as NamesContainer<
2810                tz::MzPeriodV1,
2811                (),
2812            >>::Container::new_empty(),
2813            decimal_formatter: None,
2814            _marker: PhantomData,
2815        }
2816    }
2817
2818    pub(crate) fn as_borrowed(&self) -> RawDateTimeNamesBorrowed<'_> {
2819        RawDateTimeNamesBorrowed {
2820            year_names: self.year_names.get().inner,
2821            month_names: self.month_names.get().inner,
2822            weekday_names: self.weekday_names.get().inner,
2823            dayperiod_names: self.dayperiod_names.get().inner,
2824            zone_essentials: self.zone_essentials.get().inner,
2825            locations_root: self.locations_root.get().inner,
2826            locations: self.locations.get().inner,
2827            exemplars_root: self.exemplars_root.get().inner,
2828            exemplars: self.exemplars.get().inner,
2829            mz_generic_long: self.mz_generic_long.get().inner,
2830            mz_generic_short: self.mz_generic_short.get().inner,
2831            mz_standard_long: self.mz_standard_long.get().inner,
2832            mz_specific_long: self.mz_specific_long.get().inner,
2833            mz_specific_short: self.mz_specific_short.get().inner,
2834            mz_periods: self.mz_periods.get().inner,
2835            decimal_formatter: self.decimal_formatter.as_ref(),
2836        }
2837    }
2838
2839    pub(crate) fn load_year_names<P>(
2840        &mut self,
2841        provider: &P,
2842        prefs: DateTimeFormatterPreferences,
2843        length: YearNameLength,
2844        error_field: ErrorField,
2845    ) -> Result<(), PatternLoadError>
2846    where
2847        P: BoundDataProvider<YearNamesV1> + ?Sized,
2848    {
2849        let attributes = length.to_attributes();
2850        let locale = provider
2851            .bound_marker()
2852            .make_locale(prefs.locale_preferences);
2853        let req = DataRequest {
2854            id: DataIdentifierBorrowed::for_marker_attributes_and_locale(attributes, &locale),
2855            ..Default::default()
2856        };
2857        self.year_names
2858            .load_put(provider, req, length)
2859            .map_err(|e| MaybePayloadError::into_load_error(e, error_field))?
2860            .map_err(|e| PatternLoadError::Data(e, error_field))?;
2861        Ok(())
2862    }
2863
2864    pub(crate) fn load_month_names<P>(
2865        &mut self,
2866        provider: &P,
2867        prefs: DateTimeFormatterPreferences,
2868        length: MonthNameLength,
2869        error_field: ErrorField,
2870    ) -> Result<(), PatternLoadError>
2871    where
2872        P: BoundDataProvider<MonthNamesV1> + ?Sized,
2873    {
2874        let attributes = length.to_attributes();
2875        let locale = provider
2876            .bound_marker()
2877            .make_locale(prefs.locale_preferences);
2878        let req = DataRequest {
2879            id: DataIdentifierBorrowed::for_marker_attributes_and_locale(attributes, &locale),
2880            ..Default::default()
2881        };
2882        self.month_names
2883            .load_put(provider, req, length)
2884            .map_err(|e| MaybePayloadError::into_load_error(e, error_field))?
2885            .map_err(|e| PatternLoadError::Data(e, error_field))?;
2886        Ok(())
2887    }
2888
2889    pub(crate) fn load_day_period_names<P>(
2890        &mut self,
2891        provider: &P,
2892        prefs: DateTimeFormatterPreferences,
2893        length: DayPeriodNameLength,
2894        error_field: ErrorField,
2895    ) -> Result<(), PatternLoadError>
2896    where
2897        P: BoundDataProvider<DayPeriodNamesV1> + ?Sized,
2898    {
2899        let attributes = length.to_attributes();
2900        let locale = provider
2901            .bound_marker()
2902            .make_locale(prefs.locale_preferences);
2903        let req = DataRequest {
2904            id: DataIdentifierBorrowed::for_marker_attributes_and_locale(attributes, &locale),
2905            ..Default::default()
2906        };
2907        self.dayperiod_names
2908            .load_put(provider, req, length)
2909            .map_err(|e| MaybePayloadError::into_load_error(e, error_field))?
2910            .map_err(|e| PatternLoadError::Data(e, error_field))?;
2911        Ok(())
2912    }
2913
2914    pub(crate) fn load_weekday_names<P>(
2915        &mut self,
2916        provider: &P,
2917        prefs: DateTimeFormatterPreferences,
2918        length: WeekdayNameLength,
2919        error_field: ErrorField,
2920    ) -> Result<(), PatternLoadError>
2921    where
2922        P: BoundDataProvider<WeekdayNamesV1> + ?Sized,
2923    {
2924        let attributes = length.to_attributes();
2925        let locale = provider
2926            .bound_marker()
2927            .make_locale(prefs.locale_preferences);
2928        let req = DataRequest {
2929            id: DataIdentifierBorrowed::for_marker_attributes_and_locale(attributes, &locale),
2930            ..Default::default()
2931        };
2932        self.weekday_names
2933            .load_put(provider, req, length)
2934            .map_err(|e| MaybePayloadError::into_load_error(e, error_field))?
2935            .map_err(|e| PatternLoadError::Data(e, error_field))?;
2936        Ok(())
2937    }
2938
2939    pub(crate) fn load_time_zone_essentials<P>(
2940        &mut self,
2941        provider: &P,
2942        prefs: DateTimeFormatterPreferences,
2943    ) -> Result<ErrorField, PatternLoadError>
2944    where
2945        P: BoundDataProvider<tz::EssentialsV1> + ?Sized,
2946    {
2947        let locale = provider
2948            .bound_marker()
2949            .make_locale(prefs.locale_preferences);
2950        let error_field = ErrorField(fields::Field {
2951            symbol: FieldSymbol::TimeZone(fields::TimeZone::LocalizedOffset),
2952            length: FieldLength::Four,
2953        });
2954        let variables = ();
2955        let req = DataRequest {
2956            id: DataIdentifierBorrowed::for_locale(&locale),
2957            ..Default::default()
2958        };
2959        self.zone_essentials
2960            .load_put(provider, req, variables)
2961            .map_err(|e| MaybePayloadError::into_load_error(e, error_field))?
2962            .map_err(|e| PatternLoadError::Data(e, error_field))?;
2963        Ok(error_field)
2964    }
2965
2966    pub(crate) fn load_time_zone_location_names<P, P2>(
2967        &mut self,
2968        provider: &P,
2969        root_provider: &P2,
2970        prefs: DateTimeFormatterPreferences,
2971    ) -> Result<ErrorField, PatternLoadError>
2972    where
2973        P: BoundDataProvider<tz::LocationsOverrideV1> + ?Sized,
2974        P2: BoundDataProvider<tz::LocationsRootV1> + ?Sized,
2975    {
2976        let locale = provider
2977            .bound_marker()
2978            .make_locale(prefs.locale_preferences);
2979        let error_field = ErrorField(fields::Field {
2980            symbol: FieldSymbol::TimeZone(fields::TimeZone::Location),
2981            length: FieldLength::Four,
2982        });
2983        let variables = ();
2984        let req = DataRequest {
2985            id: DataIdentifierBorrowed::for_locale(&locale),
2986            ..Default::default()
2987        };
2988        self.locations_root
2989            .load_put(root_provider, req, variables)
2990            .map_err(|e| MaybePayloadError::into_load_error(e, error_field))?
2991            .map_err(|e| PatternLoadError::Data(e, error_field))?;
2992        self.locations
2993            .load_put(provider, req, variables)
2994            .map_err(|e| MaybePayloadError::into_load_error(e, error_field))?
2995            .map_err(|e| PatternLoadError::Data(e, error_field))?;
2996        Ok(error_field)
2997    }
2998
2999    pub(crate) fn load_time_zone_exemplar_city_names<P, P2>(
3000        &mut self,
3001        provider: &P,
3002        root_provider: &P2,
3003        prefs: DateTimeFormatterPreferences,
3004    ) -> Result<ErrorField, PatternLoadError>
3005    where
3006        P: BoundDataProvider<tz::CitiesOverrideV1> + ?Sized,
3007        P2: BoundDataProvider<tz::CitiesRootV1> + ?Sized,
3008    {
3009        let locale = provider
3010            .bound_marker()
3011            .make_locale(prefs.locale_preferences);
3012        let error_field = ErrorField(fields::Field {
3013            symbol: FieldSymbol::TimeZone(fields::TimeZone::Location),
3014            length: FieldLength::Three,
3015        });
3016        let variables = ();
3017        let req = DataRequest {
3018            id: DataIdentifierBorrowed::for_locale(&locale),
3019            ..Default::default()
3020        };
3021        self.exemplars_root
3022            .load_put(root_provider, req, variables)
3023            .map_err(|e| MaybePayloadError::into_load_error(e, error_field))?
3024            .map_err(|e| PatternLoadError::Data(e, error_field))?;
3025        self.exemplars
3026            .load_put(provider, req, variables)
3027            .map_err(|e| MaybePayloadError::into_load_error(e, error_field))?
3028            .map_err(|e| PatternLoadError::Data(e, error_field))?;
3029        Ok(error_field)
3030    }
3031
3032    pub(crate) fn load_time_zone_generic_long_names(
3033        &mut self,
3034        mz_generic_provider: &(impl BoundDataProvider<tz::MzGenericLongV1> + ?Sized),
3035        mz_standard_provider: &(impl BoundDataProvider<tz::MzStandardLongV1> + ?Sized),
3036        mz_period_provider: &(impl BoundDataProvider<tz::MzPeriodV1> + ?Sized),
3037        prefs: DateTimeFormatterPreferences,
3038        names_metadata: &mut DateTimeNamesMetadata,
3039    ) -> Result<ErrorField, PatternLoadError> {
3040        let locale = mz_generic_provider
3041            .bound_marker()
3042            .make_locale(prefs.locale_preferences);
3043        let error_field = ErrorField(fields::Field {
3044            symbol: FieldSymbol::TimeZone(fields::TimeZone::GenericNonLocation),
3045            length: FieldLength::Four,
3046        });
3047        let variables = ();
3048        let req = DataRequest {
3049            id: DataIdentifierBorrowed::for_locale(&locale),
3050            ..Default::default()
3051        };
3052        let mut save_checksum = |cs: &u64| {
3053            // get_or_insert saves the value only if the Option is None.
3054            names_metadata.zone_checksum.get_or_insert(*cs);
3055        };
3056        let cs1 = self
3057            .mz_generic_long
3058            .load_put(mz_generic_provider, req, variables)
3059            .map_err(|e| MaybePayloadError::into_load_error(e, error_field))?
3060            .map_err(|e| PatternLoadError::Data(e, error_field))?
3061            .checksum
3062            .inspect(&mut save_checksum);
3063        let cs2 = self
3064            .mz_standard_long
3065            .load_put(mz_standard_provider, req, variables)
3066            .map_err(|e| MaybePayloadError::into_load_error(e, error_field))?
3067            .map_err(|e| PatternLoadError::Data(e, error_field))?
3068            .checksum
3069            .inspect(&mut save_checksum);
3070        let cs3 = self
3071            .mz_periods
3072            .load_put(mz_period_provider, Default::default(), ())
3073            .map_err(|e| MaybePayloadError::into_load_error(e, error_field))?
3074            .map_err(|e| PatternLoadError::Data(e, error_field))?
3075            .checksum
3076            .inspect(&mut save_checksum);
3077        // Error if any two of the checksum Options are Some and not equal.
3078        let cs = names_metadata.zone_checksum;
3079        if cs1.or(cs) != cs || cs2.or(cs) != cs || cs3.or(cs) != cs {
3080            return Err(PatternLoadError::Data(
3081                DataErrorKind::InconsistentData(tz::MzPeriodV1::INFO)
3082                    .with_req(tz::MzGenericLongV1::INFO, req),
3083                error_field,
3084            ));
3085        }
3086        Ok(error_field)
3087    }
3088
3089    pub(crate) fn load_time_zone_generic_short_names(
3090        &mut self,
3091        provider: &(impl BoundDataProvider<tz::MzGenericShortV1> + ?Sized),
3092        mz_period_provider: &(impl BoundDataProvider<tz::MzPeriodV1> + ?Sized),
3093        prefs: DateTimeFormatterPreferences,
3094        names_metadata: &mut DateTimeNamesMetadata,
3095    ) -> Result<ErrorField, PatternLoadError> {
3096        let locale = provider
3097            .bound_marker()
3098            .make_locale(prefs.locale_preferences);
3099        let error_field = ErrorField(fields::Field {
3100            symbol: FieldSymbol::TimeZone(fields::TimeZone::GenericNonLocation),
3101            length: FieldLength::One,
3102        });
3103        let variables = ();
3104        let req = DataRequest {
3105            id: DataIdentifierBorrowed::for_locale(&locale),
3106            ..Default::default()
3107        };
3108        let mut save_checksum = |cs: &u64| {
3109            // get_or_insert saves the value only if the Option is None.
3110            names_metadata.zone_checksum.get_or_insert(*cs);
3111        };
3112        let cs1 = self
3113            .mz_generic_short
3114            .load_put(provider, req, variables)
3115            .map_err(|e| MaybePayloadError::into_load_error(e, error_field))?
3116            .map_err(|e| PatternLoadError::Data(e, error_field))?
3117            .checksum
3118            .inspect(&mut save_checksum);
3119        let cs2 = self
3120            .mz_periods
3121            .load_put(mz_period_provider, Default::default(), ())
3122            .map_err(|e| MaybePayloadError::into_load_error(e, error_field))?
3123            .map_err(|e| PatternLoadError::Data(e, error_field))?
3124            .checksum
3125            .inspect(&mut save_checksum);
3126        // Error if any two of the checksum Options are Some and not equal.
3127        let cs = names_metadata.zone_checksum;
3128        if cs1.or(cs) != cs || cs2.or(cs) != cs {
3129            return Err(PatternLoadError::Data(
3130                DataErrorKind::InconsistentData(tz::MzPeriodV1::INFO)
3131                    .with_req(tz::MzGenericLongV1::INFO, req),
3132                error_field,
3133            ));
3134        }
3135        Ok(error_field)
3136    }
3137
3138    pub(crate) fn load_time_zone_specific_long_names(
3139        &mut self,
3140        mz_specific_provider: &(impl BoundDataProvider<tz::MzSpecificLongV1> + ?Sized),
3141        mz_standard_provider: &(impl BoundDataProvider<tz::MzStandardLongV1> + ?Sized),
3142        mz_period_provider: &(impl BoundDataProvider<tz::MzPeriodV1> + ?Sized),
3143        prefs: DateTimeFormatterPreferences,
3144        names_metadata: &mut DateTimeNamesMetadata,
3145    ) -> Result<ErrorField, PatternLoadError> {
3146        let locale = mz_specific_provider
3147            .bound_marker()
3148            .make_locale(prefs.locale_preferences);
3149        let error_field = ErrorField(fields::Field {
3150            symbol: FieldSymbol::TimeZone(fields::TimeZone::SpecificNonLocation),
3151            length: FieldLength::Four,
3152        });
3153        let variables = ();
3154        let req = DataRequest {
3155            id: DataIdentifierBorrowed::for_locale(&locale),
3156            ..Default::default()
3157        };
3158        let mut save_checksum = |cs: &u64| {
3159            // get_or_insert saves the value only if the Option is None.
3160            names_metadata.zone_checksum.get_or_insert(*cs);
3161        };
3162        let cs1 = self
3163            .mz_specific_long
3164            .load_put(mz_specific_provider, req, variables)
3165            .map_err(|e| MaybePayloadError::into_load_error(e, error_field))?
3166            .map_err(|e| PatternLoadError::Data(e, error_field))?
3167            .checksum
3168            .inspect(&mut save_checksum);
3169        let cs2 = self
3170            .mz_standard_long
3171            .load_put(mz_standard_provider, req, variables)
3172            .map_err(|e| MaybePayloadError::into_load_error(e, error_field))?
3173            .map_err(|e| PatternLoadError::Data(e, error_field))?
3174            .checksum
3175            .inspect(&mut save_checksum);
3176        let cs3 = self
3177            .mz_periods
3178            .load_put(mz_period_provider, Default::default(), ())
3179            .map_err(|e| MaybePayloadError::into_load_error(e, error_field))?
3180            .map_err(|e| PatternLoadError::Data(e, error_field))?
3181            .checksum
3182            .inspect(&mut save_checksum);
3183        // Error if any two of the checksum Options are Some and not equal.
3184        let cs = names_metadata.zone_checksum;
3185        if cs1.or(cs) != cs || cs2.or(cs) != cs || cs3.or(cs) != cs {
3186            return Err(PatternLoadError::Data(
3187                DataErrorKind::InconsistentData(tz::MzPeriodV1::INFO)
3188                    .with_req(tz::MzSpecificLongV1::INFO, req),
3189                error_field,
3190            ));
3191        }
3192        Ok(error_field)
3193    }
3194
3195    pub(crate) fn load_time_zone_specific_short_names(
3196        &mut self,
3197        provider: &(impl BoundDataProvider<tz::MzSpecificShortV1> + ?Sized),
3198        mz_period_provider: &(impl BoundDataProvider<tz::MzPeriodV1> + ?Sized),
3199        prefs: DateTimeFormatterPreferences,
3200        names_metadata: &mut DateTimeNamesMetadata,
3201    ) -> Result<ErrorField, PatternLoadError> {
3202        let locale = provider
3203            .bound_marker()
3204            .make_locale(prefs.locale_preferences);
3205        let error_field = ErrorField(fields::Field {
3206            symbol: FieldSymbol::TimeZone(fields::TimeZone::SpecificNonLocation),
3207            length: FieldLength::One,
3208        });
3209        let variables = ();
3210        let req = DataRequest {
3211            id: DataIdentifierBorrowed::for_locale(&locale),
3212            ..Default::default()
3213        };
3214        let mut save_checksum = |cs: &u64| {
3215            // get_or_insert saves the value only if the Option is None.
3216            names_metadata.zone_checksum.get_or_insert(*cs);
3217        };
3218        let cs1 = self
3219            .mz_specific_short
3220            .load_put(provider, req, variables)
3221            .map_err(|e| MaybePayloadError::into_load_error(e, error_field))?
3222            .map_err(|e| PatternLoadError::Data(e, error_field))?
3223            .checksum
3224            .inspect(&mut save_checksum);
3225        let cs2 = self
3226            .mz_periods
3227            .load_put(mz_period_provider, Default::default(), ())
3228            .map_err(|e| MaybePayloadError::into_load_error(e, error_field))?
3229            .map_err(|e| PatternLoadError::Data(e, error_field))?
3230            .checksum
3231            .inspect(&mut save_checksum);
3232        // Error if any two of the checksum Options are Some and not equal.
3233        let cs = names_metadata.zone_checksum;
3234        if cs1.or(cs) != cs || cs2.or(cs) != cs {
3235            return Err(PatternLoadError::Data(
3236                DataErrorKind::InconsistentData(tz::MzPeriodV1::INFO)
3237                    .with_req(tz::MzSpecificShortV1::INFO, req),
3238                error_field,
3239            ));
3240        }
3241        Ok(error_field)
3242    }
3243
3244    pub(crate) fn load_time_zone_field_z_except_decimals(
3245        &mut self,
3246        zone_essentials_provider: &(impl BoundDataProvider<tz::EssentialsV1> + ?Sized),
3247        mz_specific_short_provider: &(impl BoundDataProvider<tz::MzSpecificShortV1> + ?Sized),
3248        mz_period_provider: &(impl BoundDataProvider<tz::MzPeriodV1> + ?Sized),
3249        prefs: DateTimeFormatterPreferences,
3250        names_metadata: &mut DateTimeNamesMetadata,
3251    ) -> Result<ErrorField, PatternLoadError> {
3252        self.load_time_zone_essentials(zone_essentials_provider, prefs)?;
3253        self.load_time_zone_specific_short_names(
3254            mz_specific_short_provider,
3255            mz_period_provider,
3256            prefs,
3257            names_metadata,
3258        )
3259    }
3260
3261    #[expect(clippy::too_many_arguments)]
3262    pub(crate) fn load_time_zone_field_zzzz_except_decimals(
3263        &mut self,
3264        zone_essentials_provider: &(impl BoundDataProvider<tz::EssentialsV1> + ?Sized),
3265        locations_provider: &(impl BoundDataProvider<tz::LocationsOverrideV1> + ?Sized),
3266        locations_root_provider: &(impl BoundDataProvider<tz::LocationsRootV1> + ?Sized),
3267        mz_standard_long_provider: &(impl BoundDataProvider<tz::MzStandardLongV1> + ?Sized),
3268        mz_specific_long_provider: &(impl BoundDataProvider<tz::MzSpecificLongV1> + ?Sized),
3269        mz_period_provider: &(impl BoundDataProvider<tz::MzPeriodV1> + ?Sized),
3270        prefs: DateTimeFormatterPreferences,
3271        names_metadata: &mut DateTimeNamesMetadata,
3272    ) -> Result<ErrorField, PatternLoadError> {
3273        self.load_time_zone_essentials(zone_essentials_provider, prefs)?;
3274        self.load_time_zone_location_names(locations_provider, locations_root_provider, prefs)?;
3275        self.load_time_zone_specific_long_names(
3276            mz_specific_long_provider,
3277            mz_standard_long_provider,
3278            mz_period_provider,
3279            prefs,
3280            names_metadata,
3281        )
3282    }
3283
3284    #[expect(clippy::too_many_arguments)] // internal function with lots of generics
3285    pub(crate) fn load_time_zone_field_v_except_decimals(
3286        &mut self,
3287        zone_essentials_provider: &(impl BoundDataProvider<tz::EssentialsV1> + ?Sized),
3288        locations_provider: &(impl BoundDataProvider<tz::LocationsOverrideV1> + ?Sized),
3289        locations_root_provider: &(impl BoundDataProvider<tz::LocationsRootV1> + ?Sized),
3290        mz_generic_short_provider: &(impl BoundDataProvider<tz::MzGenericShortV1> + ?Sized),
3291        mz_period_provider: &(impl BoundDataProvider<tz::MzPeriodV1> + ?Sized),
3292        prefs: DateTimeFormatterPreferences,
3293        names_metadata: &mut DateTimeNamesMetadata,
3294    ) -> Result<ErrorField, PatternLoadError> {
3295        self.load_time_zone_essentials(zone_essentials_provider, prefs)?;
3296        // For fallback:
3297        self.load_time_zone_location_names(locations_provider, locations_root_provider, prefs)?;
3298        self.load_time_zone_generic_short_names(
3299            mz_generic_short_provider,
3300            mz_period_provider,
3301            prefs,
3302            names_metadata,
3303        )
3304    }
3305
3306    #[expect(clippy::too_many_arguments)]
3307    pub(crate) fn load_time_zone_field_vvvv_except_decimals(
3308        &mut self,
3309        zone_essentials_provider: &(impl BoundDataProvider<tz::EssentialsV1> + ?Sized),
3310        locations_provider: &(impl BoundDataProvider<tz::LocationsOverrideV1> + ?Sized),
3311        locations_root_provider: &(impl BoundDataProvider<tz::LocationsRootV1> + ?Sized),
3312        mz_generic_long_provider: &(impl BoundDataProvider<tz::MzGenericLongV1> + ?Sized),
3313        mz_standard_long_provider: &(impl BoundDataProvider<tz::MzStandardLongV1> + ?Sized),
3314        mz_period_provider: &(impl BoundDataProvider<tz::MzPeriodV1> + ?Sized),
3315        prefs: DateTimeFormatterPreferences,
3316        names_metadata: &mut DateTimeNamesMetadata,
3317    ) -> Result<ErrorField, PatternLoadError> {
3318        self.load_time_zone_essentials(zone_essentials_provider, prefs)?;
3319        // For fallback:
3320        self.load_time_zone_location_names(locations_provider, locations_root_provider, prefs)?;
3321        self.load_time_zone_generic_long_names(
3322            mz_generic_long_provider,
3323            mz_standard_long_provider,
3324            mz_period_provider,
3325            prefs,
3326            names_metadata,
3327        )
3328    }
3329
3330    #[allow(non_snake_case)] // this is a private function named after the case-sensitive CLDR field
3331    pub(crate) fn load_time_zone_field_V(
3332        &mut self,
3333        _prefs: DateTimeFormatterPreferences,
3334    ) -> Result<(), PatternLoadError> {
3335        // no data required
3336        Ok(())
3337    }
3338
3339    #[allow(non_snake_case)] // this is a private function named after the case-sensitive CLDR field
3340    pub(crate) fn load_time_zone_field_VVV(
3341        &mut self,
3342        locations_provider: &(impl BoundDataProvider<tz::LocationsOverrideV1> + ?Sized),
3343        locations_root_provider: &(impl BoundDataProvider<tz::LocationsRootV1> + ?Sized),
3344        exemplar_cities_provider: &(impl BoundDataProvider<tz::CitiesOverrideV1> + ?Sized),
3345        exemplar_cities_root_provider: &(impl BoundDataProvider<tz::CitiesRootV1> + ?Sized),
3346        prefs: DateTimeFormatterPreferences,
3347    ) -> Result<ErrorField, PatternLoadError> {
3348        self.load_time_zone_location_names(locations_provider, locations_root_provider, prefs)?;
3349        self.load_time_zone_exemplar_city_names(
3350            exemplar_cities_provider,
3351            exemplar_cities_root_provider,
3352            prefs,
3353        )
3354    }
3355
3356    #[allow(non_snake_case)] // this is a private function named after the case-sensitive CLDR field
3357    pub(crate) fn load_time_zone_field_VVVV_except_decimals(
3358        &mut self,
3359        zone_essentials_provider: &(impl BoundDataProvider<tz::EssentialsV1> + ?Sized),
3360        locations_provider: &(impl BoundDataProvider<tz::LocationsOverrideV1> + ?Sized),
3361        locations_root_provider: &(impl BoundDataProvider<tz::LocationsRootV1> + ?Sized),
3362        prefs: DateTimeFormatterPreferences,
3363    ) -> Result<ErrorField, PatternLoadError> {
3364        self.load_time_zone_essentials(zone_essentials_provider, prefs)?;
3365        self.load_time_zone_location_names(locations_provider, locations_root_provider, prefs)
3366    }
3367
3368    #[allow(non_snake_case)] // this is a private function named after the case-sensitive CLDR field
3369    pub(crate) fn load_time_zone_field_O_except_decimals(
3370        &mut self,
3371        zone_essentials_provider: &(impl BoundDataProvider<tz::EssentialsV1> + ?Sized),
3372        prefs: DateTimeFormatterPreferences,
3373    ) -> Result<ErrorField, PatternLoadError> {
3374        self.load_time_zone_essentials(zone_essentials_provider, prefs)
3375    }
3376
3377    #[allow(non_snake_case)] // this is a private function named after the case-sensitive CLDR field
3378    pub(crate) fn load_time_zone_field_X(
3379        &mut self,
3380        _prefs: DateTimeFormatterPreferences,
3381    ) -> Result<(), PatternLoadError> {
3382        // no data required
3383        Ok(())
3384    }
3385
3386    pub(crate) fn load_decimal_formatter(
3387        &mut self,
3388        loader: &impl DecimalFormatterLoader,
3389        prefs: DateTimeFormatterPreferences,
3390    ) -> Result<(), DataError> {
3391        if self.decimal_formatter.is_some() {
3392            return Ok(());
3393        }
3394        let mut options = DecimalFormatterOptions::default();
3395        options.grouping_strategy = Some(GroupingStrategy::Never);
3396        self.decimal_formatter = Some(DecimalFormatterLoader::load(
3397            loader,
3398            (&prefs).into(),
3399            options,
3400        )?);
3401        Ok(())
3402    }
3403
3404    /// Loads all data required for formatting the given [`PatternItem`]s.
3405    ///
3406    /// This function has a lot of arguments because many of the arguments are generic,
3407    /// and pulling them out to an options struct would be cumbersome.
3408    #[expect(clippy::too_many_arguments)]
3409    pub(crate) fn load_for_pattern(
3410        &mut self,
3411        year_provider: &(impl BoundDataProvider<YearNamesV1> + ?Sized),
3412        month_provider: &(impl BoundDataProvider<MonthNamesV1> + ?Sized),
3413        weekday_provider: &(impl BoundDataProvider<WeekdayNamesV1> + ?Sized),
3414        dayperiod_provider: &(impl BoundDataProvider<DayPeriodNamesV1> + ?Sized),
3415        zone_essentials_provider: &(impl BoundDataProvider<tz::EssentialsV1> + ?Sized),
3416        locations_provider: &(impl BoundDataProvider<tz::LocationsOverrideV1> + ?Sized),
3417        locations_root_provider: &(impl BoundDataProvider<tz::LocationsRootV1> + ?Sized),
3418        exemplar_cities_provider: &(impl BoundDataProvider<tz::CitiesOverrideV1> + ?Sized),
3419        exemplar_cities_root_provider: &(impl BoundDataProvider<tz::CitiesRootV1> + ?Sized),
3420        mz_generic_long_provider: &(impl BoundDataProvider<tz::MzGenericLongV1> + ?Sized),
3421        mz_generic_short_provider: &(impl BoundDataProvider<tz::MzGenericShortV1> + ?Sized),
3422        mz_standard_long_provider: &(impl BoundDataProvider<tz::MzStandardLongV1> + ?Sized),
3423        mz_specific_long_provider: &(impl BoundDataProvider<tz::MzSpecificLongV1> + ?Sized),
3424        mz_specific_short_provider: &(impl BoundDataProvider<tz::MzSpecificShortV1> + ?Sized),
3425        mz_period_provider: &(impl BoundDataProvider<tz::MzPeriodV1> + ?Sized),
3426        decimal_formatter_loader: &impl DecimalFormatterLoader,
3427        prefs: DateTimeFormatterPreferences,
3428        pattern_items: impl Iterator<Item = PatternItem>,
3429        names_metadata: &mut DateTimeNamesMetadata,
3430    ) -> Result<(), PatternLoadError> {
3431        let mut numeric_field = None;
3432
3433        for item in pattern_items {
3434            let PatternItem::Field(field) = item else {
3435                continue;
3436            };
3437            let error_field = ErrorField(field);
3438
3439            use crate::provider::fields::*;
3440            use FieldLength::*;
3441            use FieldSymbol as FS;
3442
3443            match (field.symbol, field.length) {
3444                ///// Textual symbols /////
3445
3446                // G..GGGGG
3447                (FS::Era, One | Two | Three | Four | Five) => {
3448                    self.load_year_names(
3449                        year_provider,
3450                        prefs,
3451                        YearNameLength::from_field_length(field.length)
3452                            .ok_or(PatternLoadError::UnsupportedLength(error_field))?,
3453                        error_field,
3454                    )?;
3455                }
3456
3457                // U..UUUUU
3458                (FS::Year(Year::Cyclic), One | Two | Three | Four | Five) => {
3459                    numeric_field = Some(field);
3460                    self.load_year_names(
3461                        year_provider,
3462                        prefs,
3463                        YearNameLength::from_field_length(field.length)
3464                            .ok_or(PatternLoadError::UnsupportedLength(error_field))?,
3465                        error_field,
3466                    )?;
3467                }
3468
3469                // MMM..MMMMM, LLL..LLLLL
3470                (
3471                    FS::Month(field_symbol @ Month::Format | field_symbol @ Month::StandAlone),
3472                    Three | Four | Five,
3473                ) => {
3474                    self.load_month_names(
3475                        month_provider,
3476                        prefs,
3477                        MonthNameLength::from_field(field_symbol, field.length)
3478                            .ok_or(PatternLoadError::UnsupportedLength(error_field))?,
3479                        error_field,
3480                    )?;
3481                }
3482
3483                // e..ee, c..cc
3484                (FS::Weekday(Weekday::Local | Weekday::StandAlone), One | Two) => {
3485                    // TODO(#5643): Requires locale-aware day-of-week calculation
3486                    return Err(PatternLoadError::UnsupportedLength(ErrorField(field)));
3487                }
3488
3489                // E..EEEEEE, eee..eeeeee, ccc..cccccc
3490                (FS::Weekday(field_symbol), One | Two | Three | Four | Five | Six) => {
3491                    self.load_weekday_names(
3492                        weekday_provider,
3493                        prefs,
3494                        WeekdayNameLength::from_field(field_symbol, field.length)
3495                            .ok_or(PatternLoadError::UnsupportedLength(error_field))?,
3496                        error_field,
3497                    )?;
3498                }
3499
3500                // a..aaaaa, b..bbbbb
3501                (FS::DayPeriod(field_symbol), One | Two | Three | Four | Five) => {
3502                    self.load_day_period_names(
3503                        dayperiod_provider,
3504                        prefs,
3505                        DayPeriodNameLength::from_field(field_symbol, field.length)
3506                            .ok_or(PatternLoadError::UnsupportedLength(error_field))?,
3507                        error_field,
3508                    )?;
3509                }
3510
3511                ///// Time zone symbols /////
3512
3513                // z..zzz
3514                (FS::TimeZone(TimeZone::SpecificNonLocation), One | Two | Three) => {
3515                    self.load_time_zone_field_z_except_decimals(
3516                        zone_essentials_provider,
3517                        mz_specific_short_provider,
3518                        mz_period_provider,
3519                        prefs,
3520                        names_metadata,
3521                    )?;
3522                    numeric_field = Some(field);
3523                }
3524                // zzzz
3525                (FS::TimeZone(TimeZone::SpecificNonLocation), Four) => {
3526                    self.load_time_zone_field_zzzz_except_decimals(
3527                        zone_essentials_provider,
3528                        locations_provider,
3529                        locations_root_provider,
3530                        mz_standard_long_provider,
3531                        mz_specific_long_provider,
3532                        mz_period_provider,
3533                        prefs,
3534                        names_metadata,
3535                    )?;
3536                    numeric_field = Some(field);
3537                }
3538                // v
3539                (FS::TimeZone(TimeZone::GenericNonLocation), One) => {
3540                    self.load_time_zone_field_v_except_decimals(
3541                        zone_essentials_provider,
3542                        locations_provider,
3543                        locations_root_provider,
3544                        mz_generic_short_provider,
3545                        mz_period_provider,
3546                        prefs,
3547                        names_metadata,
3548                    )?;
3549                    numeric_field = Some(field);
3550                }
3551                // vvvv
3552                (FS::TimeZone(TimeZone::GenericNonLocation), Four) => {
3553                    self.load_time_zone_field_vvvv_except_decimals(
3554                        zone_essentials_provider,
3555                        locations_provider,
3556                        locations_root_provider,
3557                        mz_generic_long_provider,
3558                        mz_standard_long_provider,
3559                        mz_period_provider,
3560                        prefs,
3561                        names_metadata,
3562                    )?;
3563                    numeric_field = Some(field);
3564                }
3565
3566                // V
3567                (FS::TimeZone(TimeZone::Location), One) => {
3568                    self.load_time_zone_field_V(prefs)?;
3569                }
3570                // VVV
3571                (FS::TimeZone(TimeZone::Location), Three) => {
3572                    self.load_time_zone_field_VVV(
3573                        locations_provider,
3574                        locations_root_provider,
3575                        exemplar_cities_provider,
3576                        exemplar_cities_root_provider,
3577                        prefs,
3578                    )?;
3579                }
3580                // VVVV
3581                (FS::TimeZone(TimeZone::Location), Four) => {
3582                    self.load_time_zone_field_VVVV_except_decimals(
3583                        zone_essentials_provider,
3584                        locations_provider,
3585                        locations_root_provider,
3586                        prefs,
3587                    )?;
3588                    numeric_field = Some(field);
3589                }
3590                // O, OOOO
3591                (FS::TimeZone(TimeZone::LocalizedOffset), One | Four) => {
3592                    self.load_time_zone_field_O_except_decimals(zone_essentials_provider, prefs)?;
3593                    numeric_field = Some(field);
3594                }
3595                // X..XXXXX, x..xxxxx
3596                (
3597                    FS::TimeZone(TimeZone::IsoWithZ | TimeZone::Iso),
3598                    One | Two | Three | Four | Five,
3599                ) => {
3600                    self.load_time_zone_field_X(prefs)?;
3601                }
3602
3603                ///// Numeric symbols /////
3604
3605                // y+
3606                (FS::Year(Year::Calendar), _) => numeric_field = Some(field),
3607                // u+
3608                (FS::Year(Year::Extended), _) => numeric_field = Some(field),
3609                // r+
3610                (FS::Year(Year::RelatedIso), _) => {
3611                    // always formats as ASCII
3612                }
3613
3614                // M..MM, L..LL
3615                (FS::Month(_), One | Two) => numeric_field = Some(field),
3616
3617                // d..dd
3618                (FS::Day(Day::DayOfMonth), One | Two) => numeric_field = Some(field),
3619                // D..DDD
3620                (FS::Day(Day::DayOfYear), One | Two | Three) => numeric_field = Some(field),
3621                // F
3622                (FS::Day(Day::DayOfWeekInMonth), One) => numeric_field = Some(field),
3623                // g
3624                (FS::Day(Day::ModifiedJulianDay), One) => numeric_field = Some(field),
3625
3626                // K..KK, h..hh, H..HH, k..kk
3627                (FS::Hour(_), One | Two) => numeric_field = Some(field),
3628
3629                // m..mm
3630                (FS::Minute, One | Two) => numeric_field = Some(field),
3631
3632                // s..ss
3633                (FS::Second(Second::Second), One | Two) => numeric_field = Some(field),
3634
3635                // A+
3636                (FS::Second(Second::MillisInDay), _) => numeric_field = Some(field),
3637
3638                // s.S+, ss.S+ (s is modelled by length, S+ by symbol)
3639                (FS::DecimalSecond(_), One | Two) => numeric_field = Some(field),
3640
3641                ///// Unsupported symbols /////
3642                _ => {
3643                    return Err(PatternLoadError::UnsupportedLength(ErrorField(field)));
3644                }
3645            }
3646        }
3647
3648        if let Some(field) = numeric_field {
3649            self.load_decimal_formatter(decimal_formatter_loader, prefs)
3650                .map_err(|e| PatternLoadError::Data(e, ErrorField(field)))?;
3651        }
3652
3653        Ok(())
3654    }
3655}
3656
3657impl RawDateTimeNamesBorrowed<'_> {
3658    pub(crate) fn get_name_for_month(
3659        &self,
3660        field_symbol: fields::Month,
3661        field_length: FieldLength,
3662        ordinal_index: u8,
3663        is_leap: bool,
3664    ) -> Result<MonthPlaceholderValue<'_>, GetNameForMonthError> {
3665        let month_name_length = MonthNameLength::from_field(field_symbol, field_length)
3666            .ok_or(GetNameForMonthError::InvalidFieldLength)?;
3667        let month_names = self
3668            .month_names
3669            .get_with_variables(month_name_length)
3670            .ok_or(GetNameForMonthError::NotLoaded)?;
3671        let month_index = usize::from(ordinal_index);
3672        let name = match month_names {
3673            MonthNames::Linear(linear) => {
3674                if is_leap {
3675                    None
3676                } else {
3677                    linear.get(month_index)
3678                }
3679            }
3680            MonthNames::LeapLinear(leap_linear) => {
3681                let num_months = leap_linear.len() / 2;
3682                if is_leap {
3683                    leap_linear.get(month_index + num_months)
3684                } else if month_index < num_months {
3685                    leap_linear.get(month_index)
3686                } else {
3687                    None
3688                }
3689            }
3690            MonthNames::LeapNumeric(leap_numeric) => {
3691                if is_leap {
3692                    return Ok(MonthPlaceholderValue::NumericPattern(leap_numeric));
3693                } else {
3694                    return Ok(MonthPlaceholderValue::Numeric);
3695                }
3696            }
3697        };
3698        // Note: Always return `false` for the second argument since neo MonthNames
3699        // knows how to handle leap months and we don't need the fallback logic
3700        name.map(MonthPlaceholderValue::PlainString)
3701            .ok_or(GetNameForMonthError::InvalidMonthCode)
3702    }
3703
3704    pub(crate) fn get_name_for_weekday(
3705        &self,
3706        field_symbol: fields::Weekday,
3707        field_length: FieldLength,
3708        day: icu_calendar::types::Weekday,
3709    ) -> Result<&str, GetNameForWeekdayError> {
3710        let weekday_name_length = WeekdayNameLength::from_field(field_symbol, field_length)
3711            .ok_or(GetNameForWeekdayError::InvalidFieldLength)?;
3712        let weekday_names = self
3713            .weekday_names
3714            .get_with_variables(weekday_name_length)
3715            .ok_or(GetNameForWeekdayError::NotLoaded)?;
3716        weekday_names
3717            .names
3718            .get((day as usize) % 7)
3719            // Note: LinearNames does not guarantee a length of 7.
3720            .ok_or(GetNameForWeekdayError::NotLoaded)
3721    }
3722
3723    /// Gets the era symbol, or `None` if data is loaded but symbol isn't found.
3724    ///
3725    /// `None` should fall back to the era code directly, if, for example,
3726    /// a japanext datetime is formatted with a `DateTimeFormat<Japanese>`
3727    pub(crate) fn get_name_for_era(
3728        &self,
3729        field_length: FieldLength,
3730        era_year: EraYear,
3731    ) -> Result<&str, GetNameForEraError> {
3732        let year_name_length = YearNameLength::from_field_length(field_length)
3733            .ok_or(GetNameForEraError::InvalidFieldLength)?;
3734        let year_names = self
3735            .year_names
3736            .get_with_variables(year_name_length)
3737            .ok_or(GetNameForEraError::NotLoaded)?;
3738
3739        match (year_names, era_year.era_index) {
3740            (YearNames::VariableEras(era_names), None) => {
3741                crate::provider::neo::get_year_name_from_map(
3742                    era_names,
3743                    era_year.era.as_str().into(),
3744                )
3745                .ok_or(GetNameForEraError::InvalidEraCode)
3746            }
3747            (YearNames::FixedEras(era_names), Some(index)) => era_names
3748                .get(index as usize)
3749                .ok_or(GetNameForEraError::InvalidEraCode),
3750            _ => Err(GetNameForEraError::InvalidEraCode),
3751        }
3752    }
3753
3754    pub(crate) fn get_name_for_cyclic(
3755        &self,
3756        field_length: FieldLength,
3757        cyclic: u8,
3758    ) -> Result<&str, GetNameForCyclicYearError> {
3759        let year_name_length = YearNameLength::from_field_length(field_length)
3760            .ok_or(GetNameForCyclicYearError::InvalidFieldLength)?;
3761        let year_names = self
3762            .year_names
3763            .get_with_variables(year_name_length)
3764            .ok_or(GetNameForCyclicYearError::NotLoaded)?;
3765
3766        let YearNames::Cyclic(cyclics) = year_names else {
3767            return Err(GetNameForCyclicYearError::InvalidYearNumber { max: 0 });
3768        };
3769
3770        cyclics
3771            .get(cyclic as usize - 1)
3772            .ok_or(GetNameForCyclicYearError::InvalidYearNumber {
3773                max: cyclics.len() as u8 + 1,
3774            })
3775    }
3776
3777    pub(crate) fn get_name_for_day_period(
3778        &self,
3779        field_symbol: fields::DayPeriod,
3780        field_length: FieldLength,
3781        hour: icu_time::Hour,
3782        is_top_of_hour: bool,
3783    ) -> Result<&str, GetNameForDayPeriodError> {
3784        use fields::DayPeriod::NoonMidnight;
3785        let day_period_name_length = DayPeriodNameLength::from_field(field_symbol, field_length)
3786            .ok_or(GetNameForDayPeriodError::InvalidFieldLength)?;
3787        let dayperiod_names = self
3788            .dayperiod_names
3789            .get_with_variables(day_period_name_length)
3790            .ok_or(GetNameForDayPeriodError::NotLoaded)?;
3791        let option_value: Option<&str> = match (field_symbol, u8::from(hour), is_top_of_hour) {
3792            (NoonMidnight, 00, true) => dayperiod_names.midnight().or_else(|| dayperiod_names.am()),
3793            (NoonMidnight, 12, true) => dayperiod_names.noon().or_else(|| dayperiod_names.pm()),
3794            (_, hour, _) if hour < 12 => dayperiod_names.am(),
3795            _ => dayperiod_names.pm(),
3796        };
3797        option_value.ok_or(GetNameForDayPeriodError::NotLoaded)
3798    }
3799}
3800
3801/// A container contains all data payloads for time zone formatting (borrowed version).
3802#[derive(Debug, Copy, Clone, Default)]
3803pub(crate) struct TimeZoneDataPayloadsBorrowed<'a> {
3804    /// The data that contains meta information about how to display content.
3805    pub(crate) essentials: Option<&'a tz::Essentials<'a>>,
3806    /// The root location names, e.g. Italy
3807    pub(crate) locations_root: Option<&'a tz::Locations<'a>>,
3808    /// The language specific location names, e.g. Italia
3809    pub(crate) locations: Option<&'a tz::Locations<'a>>,
3810    /// The root exemplar city names, e.g. Rome
3811    pub(crate) exemplars_root: Option<&'a tz::ExemplarCities<'a>>,
3812    /// The language specific exemplar names, e.g. Roma
3813    pub(crate) exemplars: Option<&'a tz::ExemplarCities<'a>>,
3814    /// The generic long metazone names, e.g. Pacific Time
3815    pub(crate) mz_generic_long: Option<&'a tz::MzGeneric<'a>>,
3816    /// The long metazone names shared between generic and standard, e.g. Gulf Standard Time
3817    pub(crate) mz_standard_long: Option<&'a tz::MzGeneric<'a>>,
3818    /// The generic short metazone names, e.g. PT
3819    pub(crate) mz_generic_short: Option<&'a tz::MzGeneric<'a>>,
3820    /// The specific long metazone names, e.g. Pacific Daylight Time
3821    pub(crate) mz_specific_long: Option<&'a tz::MzSpecific<'a>>,
3822    /// The specific short metazone names, e.g. Pacific Daylight Time
3823    pub(crate) mz_specific_short: Option<&'a tz::MzSpecific<'a>>,
3824    /// The metazone lookup
3825    pub(crate) mz_periods: Option<&'a tz::MzPeriod<'a>>,
3826}
3827
3828impl<'data> RawDateTimeNamesBorrowed<'data> {
3829    pub(crate) fn get_payloads(&self) -> TimeZoneDataPayloadsBorrowed<'data> {
3830        TimeZoneDataPayloadsBorrowed {
3831            essentials: self.zone_essentials.get_option(),
3832            locations_root: self.locations_root.get_option(),
3833            locations: self.locations.get_option(),
3834            exemplars: self.exemplars.get_option(),
3835            exemplars_root: self.exemplars_root.get_option(),
3836            mz_generic_long: self.mz_generic_long.get_option(),
3837            mz_standard_long: self.mz_standard_long.get_option(),
3838            mz_generic_short: self.mz_generic_short.get_option(),
3839            mz_specific_long: self.mz_specific_long.get_option(),
3840            mz_specific_short: self.mz_specific_short.get_option(),
3841            mz_periods: self.mz_periods.get_option(),
3842        }
3843    }
3844}