• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

zbraniecki / icu4x / 11904027177

19 Nov 2024 12:33AM UTC coverage: 75.477% (+0.3%) from 75.174%
11904027177

push

github

web-flow
Move DateTimePattern into pattern module (#5834)

#1317

Also removes `NeoNeverMarker` and fixes #5689

258 of 319 new or added lines in 6 files covered. (80.88%)

6967 existing lines in 278 files now uncovered.

54522 of 72237 relevant lines covered (75.48%)

655305.49 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

52.8
/components/datetime/src/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

5
use super::{
6
    DateTimePattern, DateTimePatternFormatter, GetNameForDayPeriodError, GetNameForMonthError,
7
    GetNameForWeekdayError, GetSymbolForCyclicYearError, GetSymbolForEraError,
8
    MonthPlaceholderValue, PatternLoadError,
9
};
10
use crate::external_loaders::*;
11
use crate::fields::{self, FieldLength, FieldSymbol};
12
use crate::fieldset::dynamic::CompositeDateTimeFieldSet;
13
use crate::input;
14
use crate::provider::neo::*;
15
use crate::provider::pattern::PatternItem;
16
use crate::provider::time_zones::tz;
17
use crate::scaffold::*;
18
use crate::size_test_macro::size_test;
19
use core::fmt;
20
use core::marker::PhantomData;
21
use core::num::NonZeroU8;
22
use icu_calendar::types::FormattingEra;
23
use icu_calendar::types::MonthCode;
24
use icu_decimal::options::FixedDecimalFormatterOptions;
25
use icu_decimal::options::GroupingStrategy;
26
use icu_decimal::provider::{DecimalDigitsV1Marker, DecimalSymbolsV2Marker};
27
use icu_decimal::FixedDecimalFormatter;
28
use icu_provider::prelude::*;
29

30
size_test!(
31
    TypedDateTimeNames<icu_calendar::Gregorian>,
32
    typed_date_time_names_size,
33
    344
34
);
35

36
/// A low-level type that formats datetime patterns with localized names.
37
/// The calendar should be chosen at compile time.
38
///
39
#[doc = typed_date_time_names_size!()]
40
///
41
/// Type parameters:
42
///
43
/// 1. The calendar chosen at compile time for additional type safety
44
/// 2. A components object type containing the fields that might be formatted
45
///
46
/// By default, the components object is set to [`CompositeDateTimeFieldSet`],
47
/// meaning that dates and times, but not time zones, are supported. A smaller
48
/// components object results in smaller stack size.
49
///
50
/// To support all fields including time zones, use [`CompositeFieldSet`].
51
///
52
/// [`CompositeFieldSet`]: crate::fieldset::dynamic::CompositeFieldSet
53
/// [`CompositeDateTimeFieldSet`]: crate::fieldset::dynamic::CompositeDateTimeFieldSet
54
///
55
/// # Examples
56
///
57
/// ```
58
/// use icu::calendar::Gregorian;
59
/// use icu::calendar::DateTime;
60
/// use icu::datetime::pattern::TypedDateTimeNames;
61
/// use icu::datetime::fields::FieldLength;
62
/// use icu::datetime::fields;
63
/// use icu::datetime::pattern::DateTimePattern;
64
/// use icu::locale::locale;
65
/// use writeable::assert_try_writeable_eq;
66
///
67
/// // Create an instance that can format abbreviated month, weekday, and day period names:
68
/// let mut names: TypedDateTimeNames<Gregorian> =
69
///     TypedDateTimeNames::try_new(&locale!("uk").into()).unwrap();
70
/// names
71
///     .include_month_names(fields::Month::Format, FieldLength::Three)
72
///     .unwrap()
73
///     .include_weekday_names(fields::Weekday::Format, FieldLength::Three)
74
///     .unwrap()
75
///     .include_day_period_names(FieldLength::Three)
76
///     .unwrap();
77
///
78
/// // Create a pattern from a pattern string (note: K is the hour with h11 hour cycle):
79
/// let pattern_str = "E MMM d y -- K:mm a";
80
/// let pattern: DateTimePattern = pattern_str.parse().unwrap();
81
///
82
/// // Test it:
83
/// let datetime = DateTime::try_new_gregorian(2023, 11, 20, 12, 35, 3).unwrap();
84
/// assert_try_writeable_eq!(names.with_pattern(&pattern).format(&datetime), "пн лист. 20 2023 -- 0:35 пп");
85
/// ```
86
///
87
/// If the correct data is not loaded, and error will occur:
88
///
89
/// ```
90
/// use icu::calendar::Gregorian;
91
/// use icu::calendar::{Date, Time};
92
/// use icu::datetime::DateTimeWriteError;
93
/// use icu::datetime::pattern::TypedDateTimeNames;
94
/// use icu::datetime::fields::{Field, FieldLength, FieldSymbol, Weekday};
95
/// use icu::datetime::pattern::DateTimePattern;
96
/// use icu::datetime::fieldset::dynamic::CompositeFieldSet;
97
/// use icu::locale::locale;
98
/// use icu::timezone::{TimeZoneInfo, IxdtfParser};
99
/// use writeable::{Part, assert_try_writeable_parts_eq};
100
///
101
/// // Create an instance that can format all fields (CompositeFieldSet):
102
/// let mut names: TypedDateTimeNames<Gregorian, CompositeFieldSet> =
103
///     TypedDateTimeNames::try_new(&locale!("en").into()).unwrap();
104
///
105
/// // Create a pattern from a pattern string:
106
/// let pattern_str = "'It is:' E MMM d y G 'at' h:mm:ssSSS a zzzz";
107
/// let pattern: DateTimePattern = pattern_str.parse().unwrap();
108
///
109
/// // The pattern string contains lots of symbols including "E", "MMM", and "a",
110
/// // but we did not load any data!
111
///
112
/// let mut dtz = IxdtfParser::new().try_from_str("2023-11-20T11:35:03+00:00[Europe/London]").unwrap().to_calendar(Gregorian);
113
///
114
/// // Missing data is filled in on a best-effort basis, and an error is signaled.
115
/// assert_try_writeable_parts_eq!(
116
///     names.with_pattern(&pattern).format(&dtz),
117
///     "It is: mon M11 20 2023 CE at 11:35:03.000 AM +0000",
118
///     Err(DateTimeWriteError::NamesNotLoaded(Field { symbol: FieldSymbol::Weekday(Weekday::Format), length: FieldLength::One })),
119
///     [
120
///         (7, 10, Part::ERROR), // mon
121
///         (11, 14, Part::ERROR), // M11
122
///         (23, 25, Part::ERROR), // CE
123
///         (42, 44, Part::ERROR), // AM
124
///         (45, 50, Part::ERROR), // +0000
125
///     ]
126
/// );
127
/// ```
128
///
129
/// If the pattern contains fields inconsistent with the receiver, an error will occur:
130
///
131
/// ```
132
/// use icu::calendar::Gregorian;
133
/// use icu::calendar::DateTime;
134
/// use icu::datetime::DateTimeWriteError;
135
/// use icu::datetime::pattern::TypedDateTimeNames;
136
/// use icu::datetime::fields::{Field, FieldLength, FieldSymbol, Weekday};
137
/// use icu::datetime::pattern::DateTimePattern;
138
/// use icu::datetime::fieldset::O;
139
/// use icu::locale::locale;
140
/// use icu::timezone::TimeZoneInfo;
141
/// use writeable::{Part, assert_try_writeable_parts_eq};
142
///
143
/// // Create an instance that can format abbreviated month, weekday, and day period names:
144
/// let mut names: TypedDateTimeNames<Gregorian, O> =
145
///     TypedDateTimeNames::try_new(&locale!("en").into()).unwrap();
146
///
147
/// // Create a pattern from a pattern string:
148
/// let pattern_str = "'It is:' E MMM d y G 'at' h:mm:ssSSS a zzzz";
149
/// let pattern: DateTimePattern = pattern_str.parse().unwrap();
150
///
151
/// // The pattern string contains lots of symbols including "E", "MMM", and "a",
152
/// // but the `TypedDateTimeNames` is configured to format only time zones!
153
/// // Further, the time zone we provide doesn't contain any offset into!
154
/// // Missing data is filled in on a best-effort basis, and an error is signaled.
155
/// assert_try_writeable_parts_eq!(
156
///     names.with_pattern(&pattern).format(&TimeZoneInfo::unknown()),
157
///     "It is: {E} {M} {d} {y} {G} at {h}:{m}:{s} {a} {z}",
158
///     Err(DateTimeWriteError::MissingInputField("iso_weekday")),
159
///     [
160
///         (7, 10, Part::ERROR), // {E}
161
///         (11, 14, Part::ERROR), // {M}
162
///         (15, 18, Part::ERROR), // {d}
163
///         (19, 22, Part::ERROR), // {y}
164
///         (23, 26, Part::ERROR), // {G}
165
///         (30, 33, Part::ERROR), // {h}
166
///         (34, 37, Part::ERROR), // {m}
167
///         (38, 41, Part::ERROR), // {s}
168
///         (42, 45, Part::ERROR), // {a}
169
///         (46, 49, Part::ERROR), // {z}
170
///     ]
171
/// );
172
/// ```
173
#[derive(Debug)]
174
pub struct TypedDateTimeNames<C: CldrCalendar, R: DateTimeNamesMarker = CompositeDateTimeFieldSet> {
175
    locale: DataLocale,
176
    inner: RawDateTimeNames<R>,
177
    _calendar: PhantomData<C>,
178
}
179

180
pub(crate) struct RawDateTimeNames<R: DateTimeNamesMarker> {
181
    year_names:
182
        <R::YearNames as DateTimeNamesHolderTrait<YearNamesV1Marker>>::Container<FieldLength>,
183
    month_names: <R::MonthNames as DateTimeNamesHolderTrait<MonthNamesV1Marker>>::Container<(
184
        fields::Month,
185
        FieldLength,
186
    )>,
187
    weekday_names: <R::WeekdayNames as DateTimeNamesHolderTrait<WeekdayNamesV1Marker>>::Container<
188
        (fields::Weekday, FieldLength),
189
    >,
190
    dayperiod_names:
191
        <R::DayPeriodNames as DateTimeNamesHolderTrait<DayPeriodNamesV1Marker>>::Container<
192
            FieldLength,
193
        >,
194
    zone_essentials:
195
        <R::ZoneEssentials as DateTimeNamesHolderTrait<tz::EssentialsV1Marker>>::Container<()>,
196
    locations_root:
197
        <R::ZoneLocations as DateTimeNamesHolderTrait<tz::LocationsV1Marker>>::Container<()>,
198
    locations: <R::ZoneLocations as DateTimeNamesHolderTrait<tz::LocationsV1Marker>>::Container<()>,
199
    mz_generic_long:
200
        <R::ZoneGenericLong as DateTimeNamesHolderTrait<tz::MzGenericLongV1Marker>>::Container<()>,
201
    mz_generic_short: <R::ZoneGenericShort as DateTimeNamesHolderTrait<
202
        tz::MzGenericShortV1Marker,
203
    >>::Container<()>,
204
    mz_specific_long: <R::ZoneSpecificLong as DateTimeNamesHolderTrait<
205
        tz::MzSpecificLongV1Marker,
206
    >>::Container<()>,
207
    mz_specific_short: <R::ZoneSpecificShort as DateTimeNamesHolderTrait<
208
        tz::MzSpecificShortV1Marker,
209
    >>::Container<()>,
210
    mz_periods: <R::MetazoneLookup as DateTimeNamesHolderTrait<tz::MzPeriodV1Marker>>::Container<()>,
211
    // TODO(#4340): Make the FixedDecimalFormatter optional
212
    fixed_decimal_formatter: Option<FixedDecimalFormatter>,
213
    _marker: PhantomData<R>,
214
}
215

216
// Need a custom impl because not all of the associated types impl Debug
217
impl<R: DateTimeNamesMarker> fmt::Debug for RawDateTimeNames<R> {
218
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
219
        f.debug_struct("RawDateTimeNames")
220
            .field("year_names", &self.year_names)
221
            .field("month_names", &self.month_names)
222
            .field("weekday_names", &self.weekday_names)
223
            .field("dayperiod_names", &self.dayperiod_names)
224
            .field("zone_essentials", &self.zone_essentials)
225
            .field("locations", &self.locations)
226
            .field("mz_generic_long", &self.mz_generic_long)
227
            .field("mz_generic_short", &self.mz_generic_short)
228
            .field("mz_specific_long", &self.mz_specific_long)
229
            .field("mz_specific_short", &self.mz_specific_short)
230
            .field("fixed_decimal_formatter", &self.fixed_decimal_formatter)
231
            .finish()
232
    }
233
}
234

235
#[derive(Debug, Copy, Clone)]
×
236
pub(crate) struct RawDateTimeNamesBorrowed<'l> {
237
    year_names: OptionalNames<FieldLength, &'l YearNamesV1<'l>>,
238
    month_names: OptionalNames<(fields::Month, FieldLength), &'l MonthNamesV1<'l>>,
×
239
    weekday_names: OptionalNames<(fields::Weekday, FieldLength), &'l LinearNamesV1<'l>>,
×
240
    dayperiod_names: OptionalNames<FieldLength, &'l LinearNamesV1<'l>>,
×
241
    zone_essentials: OptionalNames<(), &'l tz::EssentialsV1<'l>>,
×
242
    locations_root: OptionalNames<(), &'l tz::LocationsV1<'l>>,
×
243
    locations: OptionalNames<(), &'l tz::LocationsV1<'l>>,
×
244
    mz_generic_long: OptionalNames<(), &'l tz::MzGenericV1<'l>>,
×
245
    mz_generic_short: OptionalNames<(), &'l tz::MzGenericV1<'l>>,
×
246
    mz_specific_long: OptionalNames<(), &'l tz::MzSpecificV1<'l>>,
×
247
    mz_specific_short: OptionalNames<(), &'l tz::MzSpecificV1<'l>>,
×
248
    mz_periods: OptionalNames<(), &'l tz::MzPeriodV1<'l>>,
×
249
    pub(crate) fixed_decimal_formatter: Option<&'l FixedDecimalFormatter>,
×
250
}
251

252
impl<C: CldrCalendar, R: DateTimeNamesMarker> TypedDateTimeNames<C, R> {
253
    /// Constructor that takes a selected locale and creates an empty instance.
254
    ///
255
    /// For an example, see [`TypedDateTimeNames`].
256
    ///
257
    /// ✨ *Enabled with the `compiled_data` Cargo feature.*
258
    ///
259
    /// [📚 Help choosing a constructor](icu_provider::constructors)
260
    #[cfg(feature = "compiled_data")]
261
    pub fn try_new(locale: &DataLocale) -> Result<Self, DataError> {
36✔
262
        let mut names = Self {
36✔
263
            locale: locale.clone(),
36✔
264
            inner: RawDateTimeNames::new_without_number_formatting(),
36✔
265
            _calendar: PhantomData,
266
        };
×
267
        names.include_fixed_decimal_formatter()?;
36✔
268
        Ok(names)
36✔
269
    }
36✔
270

271
    #[doc = icu_provider::gen_any_buffer_unstable_docs!(UNSTABLE, Self::try_new)]
272
    pub fn try_new_unstable<P>(provider: &P, locale: &DataLocale) -> Result<Self, DataError>
273
    where
274
        P: DataProvider<DecimalSymbolsV2Marker> + DataProvider<DecimalDigitsV1Marker> + ?Sized,
275
    {
276
        let mut names = Self {
277
            locale: locale.clone(),
278
            inner: RawDateTimeNames::new_without_number_formatting(),
279
            _calendar: PhantomData,
280
        };
281
        names.load_fixed_decimal_formatter(provider)?;
282
        Ok(names)
283
    }
284

285
    /// Creates a completely empty instance, not even with number formatting.
286
    ///
287
    /// # Examples
288
    ///
289
    /// Errors occur if a number formatter is not loaded but one is required:
290
    ///
291
    /// ```
292
    /// use icu::calendar::Gregorian;
293
    /// use icu::calendar::Date;
294
    /// use icu::datetime::DateTimeWriteError;
295
    /// use icu::datetime::pattern::TypedDateTimeNames;
296
    /// use icu::datetime::fields::{Field, FieldLength, FieldSymbol, Weekday};
297
    /// use icu::datetime::pattern::DateTimePattern;
298
    /// use icu::datetime::fieldset::dynamic::DateFieldSet;
299
    /// use icu::locale::locale;
300
    /// use writeable::{Part, assert_try_writeable_parts_eq};
301
    ///
302
    /// // Create an instance that can format only date fields:
303
    /// let names: TypedDateTimeNames<Gregorian, DateFieldSet> =
304
    ///     TypedDateTimeNames::new_without_number_formatting(&locale!("en").into());
305
    ///
306
    /// // Create a pattern from a pattern string:
307
    /// let pattern_str = "'It is:' y-MM-dd";
308
    /// let pattern: DateTimePattern = pattern_str.parse().unwrap();
309
    ///
310
    /// // The pattern string contains lots of numeric symbols,
311
    /// // but we did not load any data!
312
    ///
313
    /// let date = Date::try_new_gregorian(2024, 7, 1).unwrap();
314
    ///
315
    /// // Missing data is filled in on a best-effort basis, and an error is signaled.
316
    /// // (note that the padding is ignored in this fallback mode)
317
    /// assert_try_writeable_parts_eq!(
318
    ///     names.with_pattern(&pattern).format(&date),
319
    ///     "It is: 2024-07-01",
320
    ///     Err(DateTimeWriteError::FixedDecimalFormatterNotLoaded),
321
    ///     [
322
    ///         (7, 11, Part::ERROR), // 2024
323
    ///         (12, 14, Part::ERROR), // 07
324
    ///         (15, 17, Part::ERROR), // 01
325
    ///     ]
326
    /// );
327
    /// ```
328
    pub fn new_without_number_formatting(locale: &DataLocale) -> Self {
329
        Self {
330
            locale: locale.clone(),
331
            inner: RawDateTimeNames::new_without_number_formatting(),
332
            _calendar: PhantomData,
333
        }
334
    }
335

336
    /// Loads year (era or cycle) names for the specified length.
337
    ///
338
    /// Does not support multiple field symbols or lengths. See #4337
339
    pub fn load_year_names<P>(
6✔
340
        &mut self,
341
        provider: &P,
342
        field_length: FieldLength,
343
    ) -> Result<&mut Self, PatternLoadError>
344
    where
345
        P: DataProvider<C::YearNamesV1Marker> + ?Sized,
346
    {
347
        self.inner.load_year_names(
12✔
348
            &C::YearNamesV1Marker::bind(provider),
6✔
349
            &self.locale,
6✔
350
            field_length,
351
        )?;
×
352
        Ok(self)
6✔
353
    }
6✔
354

355
    /// Includes year (era or cycle) names for the specified length.
356
    ///
357
    /// Does not support multiple field symbols or lengths. See #4337
358
    ///
359
    /// # Examples
360
    ///
361
    /// ```
362
    /// use icu::calendar::Gregorian;
363
    /// use icu::datetime::fields::FieldLength;
364
    /// use icu::datetime::pattern::PatternLoadError;
365
    /// use icu::datetime::pattern::TypedDateTimeNames;
366
    /// use icu::locale::locale;
367
    ///
368
    /// let mut names =
369
    ///     TypedDateTimeNames::<Gregorian>::try_new(&locale!("und").into())
370
    ///         .unwrap();
371
    ///
372
    /// // First length is successful:
373
    /// names.include_year_names(FieldLength::Four).unwrap();
374
    ///
375
    /// // Attempting to load the first length a second time will succeed:
376
    /// names.include_year_names(FieldLength::Four).unwrap();
377
    ///
378
    /// // But loading a new length fails:
379
    /// assert!(matches!(
380
    ///     names.include_year_names(FieldLength::Three),
381
    ///     Err(PatternLoadError::ConflictingField(_))
382
    /// ));
383
    /// ```
384
    #[cfg(feature = "compiled_data")]
385
    pub fn include_year_names(
386
        &mut self,
387
        field_length: FieldLength,
388
    ) -> Result<&mut Self, PatternLoadError>
389
    where
390
        crate::provider::Baked: icu_provider::DataProvider<<C as CldrCalendar>::YearNamesV1Marker>,
391
    {
392
        self.load_year_names(&crate::provider::Baked, field_length)
393
    }
394

395
    /// Loads month names for the specified symbol and length.
396
    ///
397
    /// Does not support multiple field symbols or lengths. See #4337
398
    pub fn load_month_names<P>(
7✔
399
        &mut self,
400
        provider: &P,
401
        field_symbol: fields::Month,
402
        field_length: FieldLength,
403
    ) -> Result<&mut Self, PatternLoadError>
404
    where
405
        P: DataProvider<C::MonthNamesV1Marker> + ?Sized,
406
    {
407
        self.inner.load_month_names(
14✔
408
            &C::MonthNamesV1Marker::bind(provider),
7✔
409
            &self.locale,
7✔
410
            field_symbol,
411
            field_length,
412
        )?;
×
413
        Ok(self)
7✔
414
    }
7✔
415

416
    /// Includes month names for the specified symbol and length.
417
    ///
418
    /// Does not support multiple field symbols or lengths. See #4337
419
    ///
420
    /// # Examples
421
    ///
422
    /// ```
423
    /// use icu::calendar::Gregorian;
424
    /// use icu::datetime::fields::FieldLength;
425
    /// use icu::datetime::pattern::PatternLoadError;
426
    /// use icu::datetime::pattern::TypedDateTimeNames;
427
    /// use icu::locale::locale;
428
    ///
429
    /// let mut names =
430
    ///     TypedDateTimeNames::<Gregorian>::try_new(&locale!("und").into())
431
    ///         .unwrap();
432
    /// let field_symbol = icu::datetime::fields::Month::Format;
433
    /// let alt_field_symbol = icu::datetime::fields::Month::StandAlone;
434
    ///
435
    /// // First length is successful:
436
    /// names
437
    ///     .include_month_names(field_symbol, FieldLength::Four)
438
    ///     .unwrap();
439
    ///
440
    /// // Attempting to load the first length a second time will succeed:
441
    /// names
442
    ///     .include_month_names(field_symbol, FieldLength::Four)
443
    ///     .unwrap();
444
    ///
445
    /// // But loading a new symbol or length fails:
446
    /// assert!(matches!(
447
    ///     names.include_month_names(alt_field_symbol, FieldLength::Four),
448
    ///     Err(PatternLoadError::ConflictingField(_))
449
    /// ));
450
    /// assert!(matches!(
451
    ///     names.include_month_names(field_symbol, FieldLength::Three),
452
    ///     Err(PatternLoadError::ConflictingField(_))
453
    /// ));
454
    /// ```
455
    #[cfg(feature = "compiled_data")]
456
    pub fn include_month_names(
457
        &mut self,
458
        field_symbol: fields::Month,
459
        field_length: FieldLength,
460
    ) -> Result<&mut Self, PatternLoadError>
461
    where
462
        crate::provider::Baked: icu_provider::DataProvider<<C as CldrCalendar>::MonthNamesV1Marker>,
463
    {
464
        self.load_month_names(&crate::provider::Baked, field_symbol, field_length)
465
    }
466

467
    /// Loads day period names for the specified length.
468
    ///
469
    /// Does not support multiple field symbols or lengths. See #4337
470
    pub fn load_day_period_names<P>(
11✔
471
        &mut self,
472
        provider: &P,
473
        field_length: FieldLength,
474
    ) -> Result<&mut Self, PatternLoadError>
475
    where
476
        P: DataProvider<DayPeriodNamesV1Marker> + ?Sized,
477
    {
478
        let provider = DayPeriodNamesV1Marker::bind(provider);
11✔
479
        self.inner
22✔
480
            .load_day_period_names(&provider, &self.locale, field_length)?;
11✔
481
        Ok(self)
11✔
482
    }
11✔
483

484
    /// Includes day period names for the specified length.
485
    ///
486
    /// Does not support multiple field symbols or lengths. See #4337
487
    ///
488
    /// # Examples
489
    ///
490
    /// ```
491
    /// use icu::calendar::Gregorian;
492
    /// use icu::datetime::fields::FieldLength;
493
    /// use icu::datetime::pattern::PatternLoadError;
494
    /// use icu::datetime::pattern::TypedDateTimeNames;
495
    /// use icu::locale::locale;
496
    ///
497
    /// let mut names =
498
    ///     TypedDateTimeNames::<Gregorian>::try_new(&locale!("und").into())
499
    ///         .unwrap();
500
    ///
501
    /// // First length is successful:
502
    /// names.include_day_period_names(FieldLength::Four).unwrap();
503
    ///
504
    /// // Attempting to load the first length a second time will succeed:
505
    /// names.include_day_period_names(FieldLength::Four).unwrap();
506
    ///
507
    /// // But loading a new length fails:
508
    /// assert!(matches!(
509
    ///     names.include_day_period_names(FieldLength::Three),
510
    ///     Err(PatternLoadError::ConflictingField(_))
511
    /// ));
512
    /// ```
513
    #[cfg(feature = "compiled_data")]
514
    pub fn include_day_period_names(
515
        &mut self,
516
        field_length: FieldLength,
517
    ) -> Result<&mut Self, PatternLoadError>
518
    where
519
        crate::provider::Baked: icu_provider::DataProvider<DayPeriodNamesV1Marker>,
520
    {
521
        self.load_day_period_names(&crate::provider::Baked, field_length)
522
    }
523

524
    /// Loads weekday names for the specified symbol and length.
525
    ///
526
    /// Does not support multiple field symbols or lengths. See #4337
527
    pub fn load_weekday_names<P>(
15✔
528
        &mut self,
529
        provider: &P,
530
        field_symbol: fields::Weekday,
531
        field_length: FieldLength,
532
    ) -> Result<&mut Self, PatternLoadError>
533
    where
534
        P: DataProvider<WeekdayNamesV1Marker> + ?Sized,
535
    {
536
        self.inner.load_weekday_names(
30✔
537
            &WeekdayNamesV1Marker::bind(provider),
15✔
538
            &self.locale,
15✔
539
            field_symbol,
540
            field_length,
541
        )?;
×
542
        Ok(self)
15✔
543
    }
15✔
544

545
    /// Includes weekday names for the specified symbol and length.
546
    ///
547
    /// Does not support multiple field symbols or lengths. See #4337
548
    ///
549
    /// # Examples
550
    ///
551
    /// ```
552
    /// use icu::calendar::Gregorian;
553
    /// use icu::datetime::fields::FieldLength;
554
    /// use icu::datetime::pattern::PatternLoadError;
555
    /// use icu::datetime::pattern::TypedDateTimeNames;
556
    /// use icu::locale::locale;
557
    ///
558
    /// let mut names =
559
    ///     TypedDateTimeNames::<Gregorian>::try_new(&locale!("und").into())
560
    ///         .unwrap();
561
    /// let field_symbol = icu::datetime::fields::Weekday::Format;
562
    /// let alt_field_symbol = icu::datetime::fields::Weekday::StandAlone;
563
    ///
564
    /// // First length is successful:
565
    /// names
566
    ///     .include_weekday_names(field_symbol, FieldLength::Four)
567
    ///     .unwrap();
568
    ///
569
    /// // Attempting to load the first length a second time will succeed:
570
    /// names
571
    ///     .include_weekday_names(field_symbol, FieldLength::Four)
572
    ///     .unwrap();
573
    ///
574
    /// // But loading a new symbol or length fails:
575
    /// assert!(matches!(
576
    ///     names.include_weekday_names(alt_field_symbol, FieldLength::Four),
577
    ///     Err(PatternLoadError::ConflictingField(_))
578
    /// ));
579
    /// assert!(matches!(
580
    ///     names.include_weekday_names(field_symbol, FieldLength::Three),
581
    ///     Err(PatternLoadError::ConflictingField(_))
582
    /// ));
583
    /// ```
584
    #[cfg(feature = "compiled_data")]
585
    pub fn include_weekday_names(
586
        &mut self,
587
        field_symbol: fields::Weekday,
588
        field_length: FieldLength,
589
    ) -> Result<&mut Self, PatternLoadError>
590
    where
591
        crate::provider::Baked: icu_provider::DataProvider<WeekdayNamesV1Marker>,
592
    {
593
        self.load_weekday_names(&crate::provider::Baked, field_symbol, field_length)
594
    }
595

596
    /// Loads shared essential patterns for time zone formatting.
597
    pub fn load_time_zone_essentials<P>(
598
        &mut self,
599
        provider: &P,
600
    ) -> Result<&mut Self, PatternLoadError>
601
    where
602
        P: DataProvider<tz::EssentialsV1Marker> + ?Sized,
603
    {
604
        self.inner
605
            .load_time_zone_essentials(&tz::EssentialsV1Marker::bind(provider), &self.locale)?;
606
        Ok(self)
607
    }
608

609
    /// Includes shared essential patterns for time zone formatting.
610
    ///
611
    /// This data should always be loaded when performing time zone formatting.
612
    /// By itself, it allows localized offset formats.
613
    ///
614
    /// # Examples
615
    ///
616
    /// ```
617
    /// use icu::calendar::Gregorian;
618
    /// use icu::datetime::pattern::DateTimePattern;
619
    /// use icu::datetime::fieldset::dynamic::ZoneFieldSet;
620
    /// use icu::datetime::pattern::TypedDateTimeNames;
621
    /// use icu::locale::locale;
622
    /// use icu::timezone::IxdtfParser;
623
    /// use writeable::assert_try_writeable_eq;
624
    ///
625
    /// let mut zone_london_winter = IxdtfParser::new().try_from_str(
626
    ///     "2024-01-01T00:00:00+00:00[Europe/London]",
627
    /// )
628
    /// .unwrap()
629
    /// .zone;
630
    /// let mut zone_london_summer = IxdtfParser::new().try_from_str(
631
    ///     "2024-07-01T00:00:00+01:00[Europe/London]",
632
    /// )
633
    /// .unwrap()
634
    /// .zone;
635
    ///
636
    /// let mut names =
637
    ///     TypedDateTimeNames::<Gregorian, ZoneFieldSet>::try_new(
638
    ///         &locale!("en-GB").into(),
639
    ///     )
640
    ///     .unwrap();
641
    ///
642
    /// names.include_time_zone_essentials().unwrap();
643
    ///
644
    /// // Create a pattern with symbol `OOOO`:
645
    /// let pattern_str = "'Your time zone is:' OOOO";
646
    /// let pattern: DateTimePattern = pattern_str.parse().unwrap();
647
    ///
648
    /// assert_try_writeable_eq!(
649
    ///     names.with_pattern(&pattern).format(&zone_london_winter),
650
    ///     "Your time zone is: GMT",
651
    /// );
652
    /// assert_try_writeable_eq!(
653
    ///     names.with_pattern(&pattern).format(&zone_london_summer),
654
    ///     "Your time zone is: GMT+01:00",
655
    /// );
656
    ///
657
    /// // Now try `V`:
658
    /// let pattern_str = "'Your time zone is:' V";
659
    /// let pattern: DateTimePattern = pattern_str.parse().unwrap();
660
    ///
661
    /// assert_try_writeable_eq!(
662
    ///     names.with_pattern(&pattern).format(&zone_london_winter),
663
    ///     "Your time zone is: gblon",
664
    /// );
665
    ///
666
    /// // Now try `Z`:
667
    /// let pattern_str = "'Your time zone is:' Z";
668
    /// let pattern: DateTimePattern = pattern_str.parse().unwrap();
669
    ///
670
    /// assert_try_writeable_eq!(
671
    ///     names.with_pattern(&pattern).format(&zone_london_winter),
672
    ///     "Your time zone is: +0000",
673
    /// );
674
    ///
675
    /// // Now try `ZZZZZ`:
676
    /// let pattern_str = "'Your time zone is:' ZZZZZ";
677
    /// let pattern: DateTimePattern = pattern_str.parse().unwrap();
678
    ///
679
    /// assert_try_writeable_eq!(
680
    ///     names.with_pattern(&pattern).format(&zone_london_winter),
681
    ///     "Your time zone is: Z",
682
    /// );
683
    /// assert_try_writeable_eq!(
684
    ///     names.with_pattern(&pattern).format(&zone_london_summer),
685
    ///     "Your time zone is: +01:00",
686
    /// );
687
    /// ```
688
    #[cfg(feature = "compiled_data")]
689
    pub fn include_time_zone_essentials(&mut self) -> Result<&mut Self, PatternLoadError>
690
    where
691
        crate::provider::Baked: icu_provider::DataProvider<tz::EssentialsV1Marker>,
692
    {
693
        self.load_time_zone_essentials(&crate::provider::Baked)
694
    }
695

696
    /// Loads location names for time zone formatting.
697
    pub fn load_time_zone_location_names<P>(
698
        &mut self,
699
        provider: &P,
700
    ) -> Result<&mut Self, PatternLoadError>
701
    where
702
        P: DataProvider<tz::LocationsV1Marker> + ?Sized,
703
    {
704
        self.inner
705
            .load_time_zone_location_names(&tz::LocationsV1Marker::bind(provider), &self.locale)?;
706
        Ok(self)
707
    }
708

709
    /// Includes location names for time zone formatting.
710
    ///
711
    /// Important: When performing manual time zone data loading, in addition to the
712
    /// specific time zone format data, also call either:
713
    ///
714
    /// - [`TypedDateTimeNames::include_time_zone_essentials`]
715
    /// - [`TypedDateTimeNames::load_time_zone_essentials`]
716
    ///
717
    /// # Examples
718
    ///
719
    /// ```
720
    /// use icu::calendar::Gregorian;
721
    /// use icu::datetime::pattern::DateTimePattern;
722
    /// use icu::datetime::fieldset::dynamic::ZoneFieldSet;
723
    /// use icu::datetime::pattern::TypedDateTimeNames;
724
    /// use icu::locale::locale;
725
    /// use icu::timezone::IxdtfParser;
726
    /// use writeable::assert_try_writeable_eq;
727
    ///
728
    /// let mut zone_london_winter = IxdtfParser::new().try_from_str(
729
    ///     "2024-01-01T00:00:00+00:00[Europe/London]",
730
    /// )
731
    /// .unwrap()
732
    /// .zone;
733
    ///
734
    /// let mut names =
735
    ///     TypedDateTimeNames::<Gregorian, ZoneFieldSet>::try_new(
736
    ///         &locale!("en-GB").into(),
737
    ///     )
738
    ///     .unwrap();
739
    ///
740
    /// names.include_time_zone_essentials().unwrap();
741
    /// names.include_time_zone_location_names().unwrap();
742
    ///
743
    /// // Try `VVVV`:
744
    /// let pattern_str = "'Your time zone is:' VVVV";
745
    /// let pattern: DateTimePattern = pattern_str.parse().unwrap();
746
    ///
747
    /// assert_try_writeable_eq!(
748
    ///     names.with_pattern(&pattern).format(&zone_london_winter),
749
    ///     "Your time zone is: UK Time",
750
    /// );
751
    /// ```
752
    #[cfg(feature = "compiled_data")]
753
    pub fn include_time_zone_location_names(&mut self) -> Result<&mut Self, PatternLoadError>
754
    where
755
        crate::provider::Baked: icu_provider::DataProvider<tz::MzGenericShortV1Marker>,
756
    {
757
        self.load_time_zone_location_names(&crate::provider::Baked)
758
    }
759

760
    /// Loads generic non-location long time zone names.
761
    pub fn load_time_zone_generic_long_names<P>(
762
        &mut self,
763
        provider: &P,
764
    ) -> Result<&mut Self, PatternLoadError>
765
    where
766
        P: DataProvider<tz::MzGenericLongV1Marker> + DataProvider<tz::MzPeriodV1Marker> + ?Sized,
767
    {
768
        self.inner.load_time_zone_generic_long_names(
769
            &tz::MzGenericLongV1Marker::bind(provider),
770
            &tz::MzPeriodV1Marker::bind(provider),
771
            &self.locale,
772
        )?;
773
        Ok(self)
774
    }
775

776
    /// Includes generic non-location long time zone names.
777
    ///
778
    /// Important: When performing manual time zone data loading, in addition to the
779
    /// specific time zone format data, also call either:
780
    ///
781
    /// - [`TypedDateTimeNames::include_time_zone_essentials`]
782
    /// - [`TypedDateTimeNames::load_time_zone_essentials`]
783
    ///
784
    /// # Examples
785
    ///
786
    /// ```
787
    /// use icu::calendar::Gregorian;
788
    /// use icu::datetime::pattern::DateTimePattern;
789
    /// use icu::datetime::fieldset::dynamic::ZoneFieldSet;
790
    /// use icu::datetime::pattern::TypedDateTimeNames;
791
    /// use icu::locale::locale;
792
    /// use icu::timezone::IxdtfParser;
793
    /// use writeable::assert_try_writeable_eq;
794
    ///
795
    /// let mut zone_london_winter = IxdtfParser::new().try_from_str(
796
    ///     "2024-01-01T00:00:00+00:00[Europe/London]",
797
    /// )
798
    /// .unwrap()
799
    /// .zone;
800
    /// let mut zone_london_summer = IxdtfParser::new().try_from_str(
801
    ///     "2024-07-01T00:00:00+01:00[Europe/London]",
802
    /// )
803
    /// .unwrap()
804
    /// .zone;
805
    ///
806
    /// let mut names =
807
    ///     TypedDateTimeNames::<Gregorian, ZoneFieldSet>::try_new(
808
    ///         &locale!("en-GB").into(),
809
    ///     )
810
    ///     .unwrap();
811
    ///
812
    /// names.include_time_zone_essentials().unwrap();
813
    /// names.include_time_zone_generic_long_names().unwrap();
814
    ///
815
    /// // Create a pattern with symbol `vvvv`:
816
    /// let pattern_str = "'Your time zone is:' vvvv";
817
    /// let pattern: DateTimePattern = pattern_str.parse().unwrap();
818
    ///
819
    /// assert_try_writeable_eq!(
820
    ///     names.with_pattern(&pattern).format(&zone_london_winter),
821
    ///     "Your time zone is: Greenwich Mean Time",
822
    /// );
823
    /// assert_try_writeable_eq!(
824
    ///     names.with_pattern(&pattern).format(&zone_london_summer),
825
    ///     "Your time zone is: Greenwich Mean Time", // TODO
826
    /// );
827
    /// ```
828
    #[cfg(feature = "compiled_data")]
829
    pub fn include_time_zone_generic_long_names(&mut self) -> Result<&mut Self, PatternLoadError>
830
    where
831
        crate::provider::Baked: icu_provider::DataProvider<tz::MzGenericLongV1Marker>,
832
    {
833
        self.load_time_zone_generic_long_names(&crate::provider::Baked)
834
    }
835

836
    /// Loads generic non-location short time zone names.
837
    pub fn load_time_zone_generic_short_names<P>(
838
        &mut self,
839
        provider: &P,
840
    ) -> Result<&mut Self, PatternLoadError>
841
    where
842
        P: DataProvider<tz::MzGenericShortV1Marker> + DataProvider<tz::MzPeriodV1Marker> + ?Sized,
843
    {
844
        self.inner.load_time_zone_generic_short_names(
845
            &tz::MzGenericShortV1Marker::bind(provider),
846
            &tz::MzPeriodV1Marker::bind(provider),
847
            &self.locale,
848
        )?;
849
        Ok(self)
850
    }
851

852
    /// Includes generic non-location short time zone names.
853
    ///
854
    /// Important: When performing manual time zone data loading, in addition to the
855
    /// specific time zone format data, also call either:
856
    ///
857
    /// - [`TypedDateTimeNames::include_time_zone_essentials`]
858
    /// - [`TypedDateTimeNames::load_time_zone_essentials`]
859
    ///
860
    /// # Examples
861
    ///
862
    /// ```
863
    /// use icu::calendar::Gregorian;
864
    /// use icu::datetime::pattern::DateTimePattern;
865
    /// use icu::datetime::fieldset::dynamic::ZoneFieldSet;
866
    /// use icu::datetime::pattern::TypedDateTimeNames;
867
    /// use icu::locale::locale;
868
    /// use icu::timezone::IxdtfParser;
869
    /// use writeable::assert_try_writeable_eq;
870
    ///
871
    /// let mut zone_london_winter = IxdtfParser::new().try_from_str(
872
    ///     "2024-01-01T00:00:00+00:00[Europe/London]",
873
    /// )
874
    /// .unwrap()
875
    /// .zone;
876
    /// let mut zone_london_summer = IxdtfParser::new().try_from_str(
877
    ///     "2024-07-01T00:00:00+01:00[Europe/London]",
878
    /// )
879
    /// .unwrap()
880
    /// .zone;
881
    ///
882
    /// let mut names =
883
    ///     TypedDateTimeNames::<Gregorian, ZoneFieldSet>::try_new(
884
    ///         &locale!("en-GB").into(),
885
    ///     )
886
    ///     .unwrap();
887
    ///
888
    /// names.include_time_zone_essentials().unwrap();
889
    /// names.include_time_zone_generic_short_names().unwrap();
890
    ///
891
    /// // Create a pattern with symbol `v`:
892
    /// let pattern_str = "'Your time zone is:' v";
893
    /// let pattern: DateTimePattern = pattern_str.parse().unwrap();
894
    ///
895
    /// assert_try_writeable_eq!(
896
    ///     names.with_pattern(&pattern).format(&zone_london_winter),
897
    ///     "Your time zone is: GMT",
898
    /// );
899
    /// assert_try_writeable_eq!(
900
    ///     names.with_pattern(&pattern).format(&zone_london_summer),
901
    ///     "Your time zone is: GMT", // TODO
902
    /// );
903
    /// ```
904
    #[cfg(feature = "compiled_data")]
905
    pub fn include_time_zone_generic_short_names(&mut self) -> Result<&mut Self, PatternLoadError>
906
    where
907
        crate::provider::Baked: icu_provider::DataProvider<tz::MzGenericShortV1Marker>,
908
    {
909
        self.load_time_zone_generic_short_names(&crate::provider::Baked)
910
    }
911

912
    /// Loads specific non-location long time zone names.
913
    pub fn load_time_zone_specific_long_names<P>(
914
        &mut self,
915
        provider: &P,
916
    ) -> Result<&mut Self, PatternLoadError>
917
    where
918
        P: DataProvider<tz::MzSpecificLongV1Marker> + DataProvider<tz::MzPeriodV1Marker> + ?Sized,
919
    {
920
        self.inner.load_time_zone_specific_long_names(
921
            &tz::MzSpecificLongV1Marker::bind(provider),
922
            &tz::MzPeriodV1Marker::bind(provider),
923
            &self.locale,
924
        )?;
925
        Ok(self)
926
    }
927

928
    /// Includes specific non-location long time zone names.
929
    ///
930
    /// Important: When performing manual time zone data loading, in addition to the
931
    /// specific time zone format data, also call either:
932
    ///
933
    /// - [`TypedDateTimeNames::include_time_zone_essentials`]
934
    /// - [`TypedDateTimeNames::load_time_zone_essentials`]
935
    ///
936
    /// # Examples
937
    ///
938
    /// ```
939
    /// use icu::calendar::Gregorian;
940
    /// use icu::datetime::pattern::DateTimePattern;
941
    /// use icu::datetime::fieldset::dynamic::ZoneFieldSet;
942
    /// use icu::datetime::pattern::TypedDateTimeNames;
943
    /// use icu::locale::locale;
944
    /// use icu::timezone::IxdtfParser;
945
    /// use writeable::assert_try_writeable_eq;
946
    ///
947
    /// let mut zone_london_winter = IxdtfParser::new().try_from_str(
948
    ///     "2024-01-01T00:00:00+00:00[Europe/London]",
949
    /// )
950
    /// .unwrap()
951
    /// .zone;
952
    /// let mut zone_london_summer = IxdtfParser::new().try_from_str(
953
    ///     "2024-07-01T00:00:00+01:00[Europe/London]",
954
    /// )
955
    /// .unwrap()
956
    /// .zone;
957
    ///
958
    /// let mut names =
959
    ///     TypedDateTimeNames::<Gregorian, ZoneFieldSet>::try_new(
960
    ///         &locale!("en-GB").into(),
961
    ///     )
962
    ///     .unwrap();
963
    ///
964
    /// names.include_time_zone_essentials().unwrap();
965
    /// names.include_time_zone_specific_long_names().unwrap();
966
    ///
967
    /// // Create a pattern with symbol `zzzz`:
968
    /// let pattern_str = "'Your time zone is:' zzzz";
969
    /// let pattern: DateTimePattern = pattern_str.parse().unwrap();
970
    ///
971
    /// assert_try_writeable_eq!(
972
    ///     names.with_pattern(&pattern).format(&zone_london_winter),
973
    ///     "Your time zone is: Greenwich Mean Time",
974
    /// );
975
    /// assert_try_writeable_eq!(
976
    ///     names.with_pattern(&pattern).format(&zone_london_summer),
977
    ///     "Your time zone is: British Summer Time",
978
    /// );
979
    /// ```
980
    #[cfg(feature = "compiled_data")]
981
    pub fn include_time_zone_specific_long_names(&mut self) -> Result<&mut Self, PatternLoadError>
982
    where
983
        crate::provider::Baked: icu_provider::DataProvider<tz::MzSpecificLongV1Marker>,
984
    {
985
        self.load_time_zone_specific_long_names(&crate::provider::Baked)
986
    }
987

988
    /// Loads specific non-location short time zone names.
989
    pub fn load_time_zone_specific_short_names<P>(
990
        &mut self,
991
        provider: &P,
992
    ) -> Result<&mut Self, PatternLoadError>
993
    where
994
        P: DataProvider<tz::MzSpecificShortV1Marker> + DataProvider<tz::MzPeriodV1Marker> + ?Sized,
995
    {
996
        self.inner.load_time_zone_specific_short_names(
997
            &tz::MzSpecificShortV1Marker::bind(provider),
998
            &tz::MzPeriodV1Marker::bind(provider),
999
            &self.locale,
1000
        )?;
1001
        Ok(self)
1002
    }
1003

1004
    /// Includes specific non-location short time zone names.
1005
    ///
1006
    /// Important: When performing manual time zone data loading, in addition to the
1007
    /// specific time zone format data, also call either:
1008
    ///
1009
    /// - [`TypedDateTimeNames::include_time_zone_essentials`]
1010
    /// - [`TypedDateTimeNames::load_time_zone_essentials`]
1011
    ///
1012
    /// # Examples
1013
    ///
1014
    /// ```
1015
    /// use icu::calendar::Gregorian;
1016
    /// use icu::datetime::pattern::DateTimePattern;
1017
    /// use icu::datetime::fieldset::dynamic::ZoneFieldSet;
1018
    /// use icu::datetime::pattern::TypedDateTimeNames;
1019
    /// use icu::locale::locale;
1020
    /// use icu::timezone::IxdtfParser;
1021
    /// use writeable::assert_try_writeable_eq;
1022
    ///
1023
    /// let mut zone_london_winter = IxdtfParser::new().try_from_str(
1024
    ///     "2024-01-01T00:00:00+00:00[Europe/London]",
1025
    /// )
1026
    /// .unwrap()
1027
    /// .zone;
1028
    /// let mut zone_london_summer = IxdtfParser::new().try_from_str(
1029
    ///     "2024-07-01T00:00:00+01:00[Europe/London]",
1030
    /// )
1031
    /// .unwrap()
1032
    /// .zone;
1033
    ///
1034
    /// let mut names =
1035
    ///     TypedDateTimeNames::<Gregorian, ZoneFieldSet>::try_new(
1036
    ///         &locale!("en-GB").into(),
1037
    ///     )
1038
    ///     .unwrap();
1039
    ///
1040
    /// names.include_time_zone_essentials().unwrap();
1041
    /// names.include_time_zone_specific_short_names().unwrap();
1042
    ///
1043
    /// // Create a pattern with symbol `z`:
1044
    /// let pattern_str = "'Your time zone is:' z";
1045
    /// let pattern: DateTimePattern = pattern_str.parse().unwrap();
1046
    ///
1047
    /// assert_try_writeable_eq!(
1048
    ///     names.with_pattern(&pattern).format(&zone_london_winter),
1049
    ///     "Your time zone is: GMT",
1050
    /// );
1051
    /// assert_try_writeable_eq!(
1052
    ///     names.with_pattern(&pattern).format(&zone_london_summer),
1053
    ///     "Your time zone is: BST",
1054
    /// );
1055
    /// ```
1056
    #[cfg(feature = "compiled_data")]
1057
    pub fn include_time_zone_specific_short_names(&mut self) -> Result<&mut Self, PatternLoadError>
1058
    where
1059
        crate::provider::Baked: icu_provider::DataProvider<tz::MzSpecificShortV1Marker>,
1060
    {
1061
        self.load_time_zone_specific_short_names(&crate::provider::Baked)
1062
    }
1063

1064
    /// Loads a [`FixedDecimalFormatter`] from a data provider.
1065
    #[inline]
1066
    pub fn load_fixed_decimal_formatter<P>(&mut self, provider: &P) -> Result<&mut Self, DataError>
1067
    where
1068
        P: DataProvider<DecimalSymbolsV2Marker> + DataProvider<DecimalDigitsV1Marker> + ?Sized,
1069
    {
1070
        self.inner
1071
            .load_fixed_decimal_formatter(&ExternalLoaderUnstable(provider), &self.locale)?;
1072
        Ok(self)
1073
    }
1074

1075
    /// Loads a [`FixedDecimalFormatter`] with compiled data.
1076
    ///
1077
    /// # Examples
1078
    ///
1079
    /// ```
1080
    /// use icu::calendar::Time;
1081
    /// use icu::datetime::pattern::DateTimePattern;
1082
    /// use icu::datetime::fieldset::dynamic::TimeFieldSet;
1083
    /// use icu::datetime::pattern::TypedDateTimeNames;
1084
    /// use icu::locale::locale;
1085
    /// use writeable::assert_try_writeable_eq;
1086
    ///
1087
    /// let locale = &locale!("bn").into();
1088
    ///
1089
    /// let mut names =
1090
    ///     TypedDateTimeNames::<(), TimeFieldSet>::try_new(&locale).unwrap();
1091
    /// names.include_fixed_decimal_formatter();
1092
    ///
1093
    /// // Create a pattern for the time, which is all numbers
1094
    /// let pattern_str = "'The current 24-hour time is:' HH:mm";
1095
    /// let pattern: DateTimePattern = pattern_str.parse().unwrap();
1096
    ///
1097
    /// let time = Time::try_new(6, 40, 33, 0).unwrap();
1098
    ///
1099
    /// assert_try_writeable_eq!(
1100
    ///     names.with_pattern(&pattern).format(&time),
1101
    ///     "The current 24-hour time is: ০৬:৪০",
1102
    /// );
1103
    /// ```
1104
    #[cfg(feature = "compiled_data")]
1105
    #[inline]
1106
    pub fn include_fixed_decimal_formatter(&mut self) -> Result<&mut Self, DataError> {
36✔
1107
        self.inner
72✔
1108
            .load_fixed_decimal_formatter(&ExternalLoaderCompiledData, &self.locale)?;
36✔
1109
        Ok(self)
36✔
1110
    }
36✔
1111

1112
    /// Associates this [`TypedDateTimeNames`] with a pattern
1113
    /// without loading additional data for that pattern.
1114
    #[inline]
1115
    pub fn with_pattern<'l>(
36✔
1116
        &'l self,
1117
        pattern: &'l DateTimePattern,
1118
    ) -> DateTimePatternFormatter<'l, C, R> {
1119
        DateTimePatternFormatter::new(pattern.as_borrowed(), self.inner.as_borrowed())
36✔
1120
    }
36✔
1121

1122
    /// Associates this [`TypedDateTimeNames`] with a datetime pattern
1123
    /// and loads all data required for that pattern.
1124
    ///
1125
    /// Does not duplicate textual field symbols. See #4337
1126
    pub fn load_for_pattern<'l, P>(
1127
        &'l mut self,
1128
        provider: &P,
1129
        pattern: &'l DateTimePattern,
1130
    ) -> Result<DateTimePatternFormatter<'l, C, R>, PatternLoadError>
1131
    where
1132
        P: DataProvider<C::YearNamesV1Marker>
1133
            + DataProvider<C::MonthNamesV1Marker>
1134
            + DataProvider<WeekdayNamesV1Marker>
1135
            + DataProvider<DayPeriodNamesV1Marker>
1136
            + DataProvider<tz::EssentialsV1Marker>
1137
            + DataProvider<tz::LocationsV1Marker>
1138
            + DataProvider<tz::MzGenericLongV1Marker>
1139
            + DataProvider<tz::MzGenericShortV1Marker>
1140
            + DataProvider<tz::MzSpecificLongV1Marker>
1141
            + DataProvider<tz::MzSpecificShortV1Marker>
1142
            + DataProvider<tz::MzPeriodV1Marker>
1143
            + DataProvider<DecimalSymbolsV2Marker>
1144
            + DataProvider<DecimalDigitsV1Marker>
1145
            + ?Sized,
1146
    {
1147
        let locale = &self.locale;
1148
        self.inner.load_for_pattern(
1149
            &C::YearNamesV1Marker::bind(provider),
1150
            &C::MonthNamesV1Marker::bind(provider),
1151
            &WeekdayNamesV1Marker::bind(provider),
1152
            &DayPeriodNamesV1Marker::bind(provider),
1153
            // TODO: Consider making time zone name loading optional here (lots of data)
1154
            &tz::EssentialsV1Marker::bind(provider),
1155
            &tz::LocationsV1Marker::bind(provider),
1156
            &tz::MzGenericLongV1Marker::bind(provider),
1157
            &tz::MzGenericShortV1Marker::bind(provider),
1158
            &tz::MzSpecificLongV1Marker::bind(provider),
1159
            &tz::MzSpecificShortV1Marker::bind(provider),
1160
            &tz::MzPeriodV1Marker::bind(provider),
1161
            &ExternalLoaderUnstable(provider),
1162
            locale,
1163
            pattern.iter_items(),
1164
        )?;
1165
        Ok(DateTimePatternFormatter::new(
1166
            pattern.as_borrowed(),
1167
            self.inner.as_borrowed(),
1168
        ))
1169
    }
1170

1171
    /// Associates this [`TypedDateTimeNames`] with a pattern
1172
    /// and includes all data required for that pattern.
1173
    ///
1174
    /// Does not support duplicate textual field symbols. See #4337
1175
    ///
1176
    /// # Examples
1177
    ///
1178
    /// ```
1179
    /// use icu::calendar::DateTime;
1180
    /// use icu::calendar::Gregorian;
1181
    /// use icu::datetime::pattern::DateTimePattern;
1182
    /// use icu::datetime::pattern::TypedDateTimeNames;
1183
    /// use icu::locale::locale;
1184
    /// use writeable::assert_try_writeable_eq;
1185
    ///
1186
    /// let mut names =
1187
    ///     TypedDateTimeNames::<Gregorian>::try_new(&locale!("en").into())
1188
    ///         .unwrap();
1189
    ///
1190
    /// // Create a pattern from a pattern string:
1191
    /// let pattern_str = "MMM d (EEEE) 'of year' y G 'at' h:mm a";
1192
    /// let pattern: DateTimePattern = pattern_str.parse().unwrap();
1193
    ///
1194
    /// // Load data for the pattern and format:
1195
    /// let datetime =
1196
    ///     DateTime::try_new_gregorian(2023, 12, 5, 17, 43, 12).unwrap();
1197
    /// assert_try_writeable_eq!(
1198
    ///     names
1199
    ///         .include_for_pattern(&pattern)
1200
    ///         .unwrap()
1201
    ///         .format(&datetime),
1202
    ///     "Dec 5 (Tuesday) of year 2023 AD at 5:43 PM"
1203
    /// );
1204
    /// ```
1205
    #[cfg(feature = "compiled_data")]
1206
    pub fn include_for_pattern<'l>(
1207
        &'l mut self,
1208
        pattern: &'l DateTimePattern,
1209
    ) -> Result<DateTimePatternFormatter<'l, C, R>, PatternLoadError>
1210
    where
1211
        crate::provider::Baked: DataProvider<C::YearNamesV1Marker>
1212
            + DataProvider<C::MonthNamesV1Marker>
1213
            + DataProvider<WeekdayNamesV1Marker>
1214
            + DataProvider<DayPeriodNamesV1Marker>
1215
            + DataProvider<tz::EssentialsV1Marker>
1216
            + DataProvider<tz::MzGenericShortV1Marker>,
1217
    {
1218
        let locale = &self.locale;
1219
        self.inner.load_for_pattern(
1220
            &C::YearNamesV1Marker::bind(&crate::provider::Baked),
1221
            &C::MonthNamesV1Marker::bind(&crate::provider::Baked),
1222
            &WeekdayNamesV1Marker::bind(&crate::provider::Baked),
1223
            &DayPeriodNamesV1Marker::bind(&crate::provider::Baked),
1224
            &tz::EssentialsV1Marker::bind(&crate::provider::Baked),
1225
            &tz::LocationsV1Marker::bind(&crate::provider::Baked),
1226
            &tz::MzGenericLongV1Marker::bind(&crate::provider::Baked),
1227
            &tz::MzGenericShortV1Marker::bind(&crate::provider::Baked),
1228
            &tz::MzSpecificLongV1Marker::bind(&crate::provider::Baked),
1229
            &tz::MzSpecificShortV1Marker::bind(&crate::provider::Baked),
1230
            &tz::MzPeriodV1Marker::bind(&crate::provider::Baked),
1231
            &ExternalLoaderCompiledData,
1232
            locale,
1233
            pattern.iter_items(),
1234
        )?;
1235
        Ok(DateTimePatternFormatter::new(
1236
            pattern.as_borrowed(),
1237
            self.inner.as_borrowed(),
1238
        ))
1239
    }
1240
}
1241

1242
impl<R: DateTimeNamesMarker> RawDateTimeNames<R> {
1243
    pub(crate) fn new_without_number_formatting() -> Self {
36✔
1244
        Self {
36✔
1245
            year_names: <R::YearNames as DateTimeNamesHolderTrait<YearNamesV1Marker>>::Container::<_>::new_empty(),
36✔
1246
            month_names: <R::MonthNames as DateTimeNamesHolderTrait<MonthNamesV1Marker>>::Container::<_>::new_empty(),
36✔
1247
            weekday_names: <R::WeekdayNames as DateTimeNamesHolderTrait<WeekdayNamesV1Marker>>::Container::<_>::new_empty(),
36✔
1248
            dayperiod_names: <R::DayPeriodNames as DateTimeNamesHolderTrait<DayPeriodNamesV1Marker>>::Container::<_>::new_empty(),
36✔
1249
            zone_essentials: <R::ZoneEssentials as DateTimeNamesHolderTrait<tz::EssentialsV1Marker>>::Container::<_>::new_empty(),
36✔
1250
            locations_root: <R::ZoneLocations as DateTimeNamesHolderTrait<tz::LocationsV1Marker>>::Container::<_>::new_empty(),
36✔
1251
            locations: <R::ZoneLocations as DateTimeNamesHolderTrait<tz::LocationsV1Marker>>::Container::<_>::new_empty(),
36✔
1252
            mz_generic_long: <R::ZoneGenericLong as DateTimeNamesHolderTrait<tz::MzGenericLongV1Marker>>::Container::<_>::new_empty(),
36✔
1253
            mz_generic_short: <R::ZoneGenericShort as DateTimeNamesHolderTrait<tz::MzGenericShortV1Marker>>::Container::<_>::new_empty(),
36✔
1254
            mz_specific_long: <R::ZoneSpecificLong as DateTimeNamesHolderTrait<tz::MzSpecificLongV1Marker>>::Container::<_>::new_empty(),
36✔
1255
            mz_specific_short: <R::ZoneSpecificShort as DateTimeNamesHolderTrait<tz::MzSpecificShortV1Marker>>::Container::<_>::new_empty(),
36✔
1256
            mz_periods: <R::MetazoneLookup as DateTimeNamesHolderTrait<tz::MzPeriodV1Marker>>::Container::<_>::new_empty(),
36✔
1257
            fixed_decimal_formatter: None,
36✔
1258
            _marker: PhantomData,
1259
        }
×
1260
    }
36✔
1261

1262
    pub(crate) fn as_borrowed(&self) -> RawDateTimeNamesBorrowed {
36✔
1263
        RawDateTimeNamesBorrowed {
36✔
1264
            year_names: self.year_names.get().inner,
36✔
1265
            month_names: self.month_names.get().inner,
36✔
1266
            weekday_names: self.weekday_names.get().inner,
36✔
1267
            dayperiod_names: self.dayperiod_names.get().inner,
36✔
1268
            zone_essentials: self.zone_essentials.get().inner,
36✔
1269
            locations_root: self.locations_root.get().inner,
36✔
1270
            locations: self.locations.get().inner,
36✔
1271
            mz_generic_long: self.mz_generic_long.get().inner,
36✔
1272
            mz_generic_short: self.mz_generic_short.get().inner,
36✔
1273
            mz_specific_long: self.mz_specific_long.get().inner,
36✔
1274
            mz_specific_short: self.mz_specific_short.get().inner,
36✔
1275
            mz_periods: self.mz_periods.get().inner,
36✔
1276
            fixed_decimal_formatter: self.fixed_decimal_formatter.as_ref(),
36✔
1277
        }
1278
    }
36✔
1279

1280
    pub(crate) fn load_year_names<P>(
6✔
1281
        &mut self,
1282
        provider: &P,
1283
        locale: &DataLocale,
1284
        field_length: FieldLength,
1285
    ) -> Result<(), PatternLoadError>
1286
    where
1287
        P: BoundDataProvider<YearNamesV1Marker> + ?Sized,
1288
    {
1289
        let field = fields::Field {
6✔
1290
            symbol: FieldSymbol::Era,
6✔
1291
            length: field_length,
1292
        };
1293
        // UTS 35 says that "G..GGG" are all Abbreviated
1294
        let field_length = field_length.numeric_to_abbr();
6✔
1295
        let variables = field_length;
1296
        let req = DataRequest {
6✔
1297
            id: DataIdentifierBorrowed::for_marker_attributes_and_locale(
6✔
1298
                marker_attrs::name_attr_for(
6✔
1299
                    marker_attrs::Context::Format,
6✔
1300
                    match field_length {
6✔
1301
                        FieldLength::Three => marker_attrs::Length::Abbr,
3✔
1302
                        FieldLength::Five => marker_attrs::Length::Narrow,
2✔
1303
                        FieldLength::Four => marker_attrs::Length::Wide,
1✔
1304
                        _ => return Err(PatternLoadError::UnsupportedLength(field)),
×
1305
                    },
1306
                ),
1307
                locale,
1308
            ),
1309
            ..Default::default()
6✔
1310
        };
1311
        self.year_names
12✔
1312
            .load_put(provider, req, variables)
NEW
1313
            .map_err(|e| MaybePayloadError::into_load_error(e, field))?
×
1314
            .map_err(PatternLoadError::Data)?;
×
1315
        Ok(())
6✔
1316
    }
6✔
1317

1318
    pub(crate) fn load_month_names<P>(
7✔
1319
        &mut self,
1320
        provider: &P,
1321
        locale: &DataLocale,
1322
        field_symbol: fields::Month,
1323
        field_length: FieldLength,
1324
    ) -> Result<(), PatternLoadError>
1325
    where
1326
        P: BoundDataProvider<MonthNamesV1Marker> + ?Sized,
1327
    {
1328
        let field = fields::Field {
7✔
1329
            symbol: FieldSymbol::Month(field_symbol),
7✔
1330
            length: field_length,
1331
        };
1332
        let variables = (field_symbol, field_length);
7✔
1333
        let req = DataRequest {
7✔
1334
            id: DataIdentifierBorrowed::for_marker_attributes_and_locale(
7✔
1335
                marker_attrs::name_attr_for(
7✔
1336
                    match field_symbol {
7✔
1337
                        fields::Month::Format => marker_attrs::Context::Format,
4✔
1338
                        fields::Month::StandAlone => marker_attrs::Context::Standalone,
3✔
1339
                    },
1340
                    match field_length {
7✔
1341
                        FieldLength::Three => marker_attrs::Length::Abbr,
2✔
1342
                        FieldLength::Five => marker_attrs::Length::Narrow,
2✔
1343
                        FieldLength::Four => marker_attrs::Length::Wide,
3✔
1344
                        _ => return Err(PatternLoadError::UnsupportedLength(field)),
×
1345
                    },
1346
                ),
1347
                locale,
1348
            ),
1349
            ..Default::default()
7✔
1350
        };
1351
        self.month_names
14✔
1352
            .load_put(provider, req, variables)
NEW
1353
            .map_err(|e| MaybePayloadError::into_load_error(e, field))?
×
1354
            .map_err(PatternLoadError::Data)?;
×
1355
        Ok(())
7✔
1356
    }
7✔
1357

1358
    pub(crate) fn load_day_period_names<P>(
11✔
1359
        &mut self,
1360
        provider: &P,
1361
        locale: &DataLocale,
1362
        field_length: FieldLength,
1363
    ) -> Result<(), PatternLoadError>
1364
    where
1365
        P: BoundDataProvider<DayPeriodNamesV1Marker> + ?Sized,
1366
    {
1367
        let field = fields::Field {
11✔
1368
            // Names for 'a' and 'b' are stored in the same data marker
1369
            symbol: FieldSymbol::DayPeriod(fields::DayPeriod::NoonMidnight),
11✔
1370
            length: field_length,
1371
        };
1372
        // UTS 35 says that "a..aaa" are all Abbreviated
1373
        let field_length = field_length.numeric_to_abbr();
11✔
1374
        let variables = field_length;
1375
        let req = DataRequest {
11✔
1376
            id: DataIdentifierBorrowed::for_marker_attributes_and_locale(
11✔
1377
                marker_attrs::name_attr_for(
11✔
1378
                    marker_attrs::Context::Format,
11✔
1379
                    match field_length {
11✔
1380
                        FieldLength::Three => marker_attrs::Length::Abbr,
7✔
1381
                        FieldLength::Five => marker_attrs::Length::Narrow,
2✔
1382
                        FieldLength::Four => marker_attrs::Length::Wide,
2✔
1383
                        _ => return Err(PatternLoadError::UnsupportedLength(field)),
×
1384
                    },
1385
                ),
1386
                locale,
1387
            ),
1388
            ..Default::default()
11✔
1389
        };
1390
        self.dayperiod_names
22✔
1391
            .load_put(provider, req, variables)
NEW
1392
            .map_err(|e| MaybePayloadError::into_load_error(e, field))?
×
1393
            .map_err(PatternLoadError::Data)?;
×
1394
        Ok(())
11✔
1395
    }
11✔
1396

1397
    pub(crate) fn load_weekday_names<P>(
15✔
1398
        &mut self,
1399
        provider: &P,
1400
        locale: &DataLocale,
1401
        field_symbol: fields::Weekday,
1402
        field_length: FieldLength,
1403
    ) -> Result<(), PatternLoadError>
1404
    where
1405
        P: BoundDataProvider<WeekdayNamesV1Marker> + ?Sized,
1406
    {
1407
        let field = fields::Field {
15✔
1408
            symbol: FieldSymbol::Weekday(field_symbol),
15✔
1409
            length: field_length,
1410
        };
1411
        // UTS 35 says that "E..EEE" are all Abbreviated
1412
        // However, this doesn't apply to "e" and "c".
1413
        let field_length = if matches!(field_symbol, fields::Weekday::Format) {
15✔
1414
            field_length.numeric_to_abbr()
11✔
1415
        } else {
1416
            field_length
4✔
1417
        };
1418
        let variables = (field_symbol, field_length);
15✔
1419
        let req = DataRequest {
15✔
1420
            id: DataIdentifierBorrowed::for_marker_attributes_and_locale(
15✔
1421
                marker_attrs::name_attr_for(
15✔
1422
                    match field_symbol {
15✔
1423
                        // UTS 35 says that "e" and "E" have the same non-numeric names
1424
                        fields::Weekday::Format | fields::Weekday::Local => {
1425
                            marker_attrs::Context::Format
11✔
1426
                        }
1427
                        fields::Weekday::StandAlone => marker_attrs::Context::Standalone,
4✔
1428
                    },
1429
                    match field_length {
15✔
1430
                        FieldLength::Three => marker_attrs::Length::Abbr,
6✔
1431
                        FieldLength::Five => marker_attrs::Length::Narrow,
3✔
1432
                        FieldLength::Four => marker_attrs::Length::Wide,
3✔
1433
                        FieldLength::Six => marker_attrs::Length::Short,
3✔
1434
                        _ => return Err(PatternLoadError::UnsupportedLength(field)),
×
1435
                    },
1436
                ),
1437
                locale,
1438
            ),
1439
            ..Default::default()
15✔
1440
        };
1441
        self.weekday_names
30✔
1442
            .load_put(provider, req, variables)
NEW
1443
            .map_err(|e| MaybePayloadError::into_load_error(e, field))?
×
1444
            .map_err(PatternLoadError::Data)?;
×
1445
        Ok(())
15✔
1446
    }
15✔
1447

1448
    pub(crate) fn load_time_zone_essentials<P>(
×
1449
        &mut self,
1450
        provider: &P,
1451
        locale: &DataLocale,
1452
    ) -> Result<(), PatternLoadError>
1453
    where
1454
        P: BoundDataProvider<tz::EssentialsV1Marker> + ?Sized,
1455
    {
1456
        let field = fields::Field {
×
1457
            symbol: FieldSymbol::TimeZone(fields::TimeZone::LocalizedOffset),
×
1458
            length: FieldLength::Four,
×
1459
        };
1460
        let variables = ();
1461
        let req = DataRequest {
×
1462
            id: DataIdentifierBorrowed::for_locale(locale),
×
1463
            ..Default::default()
×
1464
        };
1465
        self.zone_essentials
×
1466
            .load_put(provider, req, variables)
NEW
1467
            .map_err(|e| MaybePayloadError::into_load_error(e, field))?
×
1468
            .map_err(PatternLoadError::Data)?;
×
1469
        Ok(())
×
1470
    }
×
1471

1472
    pub(crate) fn load_time_zone_location_names<P>(
×
1473
        &mut self,
1474
        provider: &P,
1475
        locale: &DataLocale,
1476
    ) -> Result<(), PatternLoadError>
1477
    where
1478
        P: BoundDataProvider<tz::LocationsV1Marker> + ?Sized,
1479
    {
1480
        let field = fields::Field {
×
1481
            symbol: FieldSymbol::TimeZone(fields::TimeZone::Location),
×
1482
            length: FieldLength::Four,
×
1483
        };
1484
        let variables = ();
1485
        let req = DataRequest {
×
1486
            id: DataIdentifierBorrowed::for_locale(locale),
×
1487
            ..Default::default()
×
1488
        };
1489
        self.locations_root
×
1490
            .load_put(provider, Default::default(), variables)
×
NEW
1491
            .map_err(|e| MaybePayloadError::into_load_error(e, field))?
×
1492
            .map_err(PatternLoadError::Data)?;
×
1493
        self.locations
×
1494
            .load_put(provider, req, variables)
NEW
1495
            .map_err(|e| MaybePayloadError::into_load_error(e, field))?
×
1496
            .map_err(PatternLoadError::Data)?;
×
1497
        Ok(())
×
1498
    }
×
1499

1500
    pub(crate) fn load_time_zone_generic_long_names(
×
1501
        &mut self,
1502
        mz_generic_long_provider: &(impl BoundDataProvider<tz::MzGenericLongV1Marker> + ?Sized),
1503
        mz_period_provider: &(impl BoundDataProvider<tz::MzPeriodV1Marker> + ?Sized),
1504
        locale: &DataLocale,
1505
    ) -> Result<(), PatternLoadError> {
1506
        let field = fields::Field {
×
1507
            symbol: FieldSymbol::TimeZone(fields::TimeZone::GenericNonLocation),
×
1508
            length: FieldLength::Four,
×
1509
        };
1510
        let variables = ();
1511
        let req = DataRequest {
×
1512
            id: DataIdentifierBorrowed::for_locale(locale),
×
1513
            ..Default::default()
×
1514
        };
1515
        self.mz_generic_long
×
1516
            .load_put(mz_generic_long_provider, req, variables)
NEW
1517
            .map_err(|e| MaybePayloadError::into_load_error(e, field))?
×
1518
            .map_err(PatternLoadError::Data)?;
×
1519
        self.mz_periods
×
1520
            .load_put(mz_period_provider, Default::default(), variables)
×
NEW
1521
            .map_err(|e| MaybePayloadError::into_load_error(e, field))?
×
1522
            .map_err(PatternLoadError::Data)?;
×
1523
        Ok(())
×
1524
    }
×
1525

1526
    pub(crate) fn load_time_zone_generic_short_names(
×
1527
        &mut self,
1528
        mz_generic_short_provider: &(impl BoundDataProvider<tz::MzGenericShortV1Marker> + ?Sized),
1529
        mz_period_provider: &(impl BoundDataProvider<tz::MzPeriodV1Marker> + ?Sized),
1530
        locale: &DataLocale,
1531
    ) -> Result<(), PatternLoadError> {
1532
        let field = fields::Field {
×
1533
            symbol: FieldSymbol::TimeZone(fields::TimeZone::GenericNonLocation),
×
1534
            length: FieldLength::One,
×
1535
        };
1536
        let variables = ();
1537
        let req = DataRequest {
×
1538
            id: DataIdentifierBorrowed::for_locale(locale),
×
1539
            ..Default::default()
×
1540
        };
1541
        self.mz_generic_short
×
1542
            .load_put(mz_generic_short_provider, req, variables)
NEW
1543
            .map_err(|e| MaybePayloadError::into_load_error(e, field))?
×
1544
            .map_err(PatternLoadError::Data)?;
×
1545
        self.mz_periods
×
1546
            .load_put(mz_period_provider, Default::default(), variables)
×
NEW
1547
            .map_err(|e| MaybePayloadError::into_load_error(e, field))?
×
1548
            .map_err(PatternLoadError::Data)?;
×
1549
        Ok(())
×
1550
    }
×
1551

1552
    pub(crate) fn load_time_zone_specific_long_names(
×
1553
        &mut self,
1554
        mz_specific_long_provider: &(impl BoundDataProvider<tz::MzSpecificLongV1Marker> + ?Sized),
1555
        mz_period_provider: &(impl BoundDataProvider<tz::MzPeriodV1Marker> + ?Sized),
1556
        locale: &DataLocale,
1557
    ) -> Result<(), PatternLoadError> {
1558
        let field = fields::Field {
×
1559
            symbol: FieldSymbol::TimeZone(fields::TimeZone::SpecificNonLocation),
×
1560
            length: FieldLength::Four,
×
1561
        };
1562
        let variables = ();
1563
        let req = DataRequest {
×
1564
            id: DataIdentifierBorrowed::for_locale(locale),
×
1565
            ..Default::default()
×
1566
        };
1567
        self.mz_specific_long
×
1568
            .load_put(mz_specific_long_provider, req, variables)
NEW
1569
            .map_err(|e| MaybePayloadError::into_load_error(e, field))?
×
1570
            .map_err(PatternLoadError::Data)?;
×
1571
        self.mz_periods
×
1572
            .load_put(mz_period_provider, Default::default(), variables)
×
NEW
1573
            .map_err(|e| MaybePayloadError::into_load_error(e, field))?
×
1574
            .map_err(PatternLoadError::Data)?;
×
1575
        Ok(())
×
1576
    }
×
1577

1578
    pub(crate) fn load_time_zone_specific_short_names(
×
1579
        &mut self,
1580
        mz_specific_short_provider: &(impl BoundDataProvider<tz::MzSpecificShortV1Marker> + ?Sized),
1581
        mz_period_provider: &(impl BoundDataProvider<tz::MzPeriodV1Marker> + ?Sized),
1582
        locale: &DataLocale,
1583
    ) -> Result<(), PatternLoadError> {
1584
        let field = fields::Field {
×
1585
            symbol: FieldSymbol::TimeZone(fields::TimeZone::SpecificNonLocation),
×
1586
            length: FieldLength::One,
×
1587
        };
1588
        let variables = ();
1589
        let req = DataRequest {
×
1590
            id: DataIdentifierBorrowed::for_locale(locale),
×
1591
            ..Default::default()
×
1592
        };
1593
        self.mz_specific_short
×
1594
            .load_put(mz_specific_short_provider, req, variables)
NEW
1595
            .map_err(|e| MaybePayloadError::into_load_error(e, field))?
×
1596
            .map_err(PatternLoadError::Data)?;
×
1597
        self.mz_periods
×
1598
            .load_put(mz_period_provider, Default::default(), variables)
×
NEW
1599
            .map_err(|e| MaybePayloadError::into_load_error(e, field))?
×
1600
            .map_err(PatternLoadError::Data)?;
×
1601
        Ok(())
×
1602
    }
×
1603

1604
    pub(crate) fn load_fixed_decimal_formatter(
36✔
1605
        &mut self,
1606
        loader: &impl FixedDecimalFormatterLoader,
1607
        locale: &DataLocale,
1608
    ) -> Result<(), DataError> {
1609
        if self.fixed_decimal_formatter.is_some() {
36✔
1610
            return Ok(());
×
1611
        }
1612
        let mut options = FixedDecimalFormatterOptions::default();
36✔
1613
        options.grouping_strategy = GroupingStrategy::Never;
36✔
1614
        self.fixed_decimal_formatter =
36✔
1615
            Some(FixedDecimalFormatterLoader::load(loader, locale, options)?);
36✔
1616
        Ok(())
36✔
1617
    }
36✔
1618

1619
    /// Loads all data required for formatting the given [`PatternItem`]s.
1620
    ///
1621
    /// This function has a lot of arguments because many of the arguments are generic,
1622
    /// and pulling them out to an options struct would be cumbersome.
1623
    #[allow(clippy::too_many_arguments)]
1624
    pub(crate) fn load_for_pattern(
×
1625
        &mut self,
1626
        year_provider: &(impl BoundDataProvider<YearNamesV1Marker> + ?Sized),
1627
        month_provider: &(impl BoundDataProvider<MonthNamesV1Marker> + ?Sized),
1628
        weekday_provider: &(impl BoundDataProvider<WeekdayNamesV1Marker> + ?Sized),
1629
        dayperiod_provider: &(impl BoundDataProvider<DayPeriodNamesV1Marker> + ?Sized),
1630
        zone_essentials_provider: &(impl BoundDataProvider<tz::EssentialsV1Marker> + ?Sized),
1631
        locations_provider: &(impl BoundDataProvider<tz::LocationsV1Marker> + ?Sized),
1632
        mz_generic_long_provider: &(impl BoundDataProvider<tz::MzGenericLongV1Marker> + ?Sized),
1633
        mz_generic_short_provider: &(impl BoundDataProvider<tz::MzGenericShortV1Marker> + ?Sized),
1634
        mz_specific_long_provider: &(impl BoundDataProvider<tz::MzSpecificLongV1Marker> + ?Sized),
1635
        mz_specific_short_provider: &(impl BoundDataProvider<tz::MzSpecificShortV1Marker> + ?Sized),
1636
        mz_period_provider: &(impl BoundDataProvider<tz::MzPeriodV1Marker> + ?Sized),
1637
        fixed_decimal_formatter_loader: &impl FixedDecimalFormatterLoader,
1638
        locale: &DataLocale,
1639
        pattern_items: impl Iterator<Item = PatternItem>,
1640
    ) -> Result<(), PatternLoadError> {
1641
        let mut load_fdf = false;
×
1642

1643
        for item in pattern_items {
×
1644
            let PatternItem::Field(field) = item else {
×
1645
                continue;
1646
            };
1647

1648
            use fields::*;
1649
            use FieldLength::*;
1650
            use FieldSymbol as FS;
1651

1652
            match (field.symbol, field.length) {
×
1653
                ///// Textual symbols /////
1654

1655
                // G..GGGGG
1656
                (FS::Era, One | Two | Three | Four | Five) => {
1657
                    self.load_year_names(year_provider, locale, field.length)?;
×
1658
                }
1659

1660
                // U..UUUUU
1661
                (FS::Year(Year::Cyclic), One | Two | Three | Four | Five) => {
1662
                    load_fdf = true;
×
1663
                    self.load_year_names(year_provider, locale, field.length)?;
×
1664
                }
1665

1666
                // MMM..MMMMM
1667
                (FS::Month(Month::Format), Three | Four | Five) => {
1668
                    self.load_month_names(month_provider, locale, Month::Format, field.length)?;
×
1669
                }
1670

1671
                // LLL..LLLLL
1672
                (FS::Month(Month::StandAlone), Three | Four | Five) => {
1673
                    self.load_month_names(month_provider, locale, Month::StandAlone, field.length)?;
×
1674
                }
1675

1676
                // E..EE
1677
                (FS::Weekday(Weekday::Format), One | Two) => {
1678
                    self.load_weekday_names(
×
1679
                        weekday_provider,
1680
                        locale,
1681
                        Weekday::Format,
×
1682
                        field.length,
×
1683
                    )?;
×
1684
                }
1685
                // EEE..EEEEEE, eee..eeeeee, ccc..cccccc
1686
                (FS::Weekday(symbol), Three | Four | Five | Six) => {
×
1687
                    self.load_weekday_names(weekday_provider, locale, symbol, field.length)?;
×
1688
                }
1689

1690
                // a..aaaaa, b..bbbbb
1691
                (FS::DayPeriod(_), One | Two | Three | Four | Five) => {
1692
                    self.load_day_period_names(dayperiod_provider, locale, field.length)?;
×
1693
                }
1694

1695
                ///// Time zone symbols /////
1696

1697
                // z..zzz
1698
                (FS::TimeZone(TimeZone::SpecificNonLocation), One | Two | Three) => {
1699
                    load_fdf = true;
×
1700
                    self.load_time_zone_essentials(zone_essentials_provider, locale)?;
×
1701
                    self.load_time_zone_specific_short_names(
×
1702
                        mz_specific_short_provider,
1703
                        mz_period_provider,
1704
                        locale,
1705
                    )?;
×
1706
                }
1707
                // zzzz
1708
                (FS::TimeZone(TimeZone::SpecificNonLocation), Four) => {
1709
                    load_fdf = true;
×
1710
                    self.load_time_zone_essentials(zone_essentials_provider, locale)?;
×
1711
                    self.load_time_zone_specific_long_names(
×
1712
                        mz_specific_long_provider,
1713
                        mz_period_provider,
1714
                        locale,
1715
                    )?;
×
1716
                    self.load_time_zone_location_names(locations_provider, locale)?;
×
1717
                }
1718

1719
                // v
1720
                (FS::TimeZone(TimeZone::GenericNonLocation), One) => {
1721
                    load_fdf = true;
×
1722
                    self.load_time_zone_essentials(zone_essentials_provider, locale)?;
×
1723
                    self.load_time_zone_generic_short_names(
×
1724
                        mz_generic_short_provider,
1725
                        mz_period_provider,
1726
                        locale,
1727
                    )?;
×
1728
                    // For fallback:
1729
                    self.load_time_zone_location_names(locations_provider, locale)?;
×
1730
                }
1731
                // vvvv
1732
                (FS::TimeZone(TimeZone::GenericNonLocation), Four) => {
1733
                    load_fdf = true;
×
1734
                    self.load_time_zone_essentials(zone_essentials_provider, locale)?;
×
1735
                    self.load_time_zone_generic_long_names(
×
1736
                        mz_generic_long_provider,
1737
                        mz_period_provider,
1738
                        locale,
1739
                    )?;
×
1740
                    // For fallback:
1741
                    self.load_time_zone_location_names(locations_provider, locale)?;
×
1742
                }
1743

1744
                // V
1745
                (FS::TimeZone(TimeZone::Location), One) => {
1746
                    // no data required
1747
                }
1748
                // VVVV
1749
                (FS::TimeZone(TimeZone::Location), Four) => {
1750
                    load_fdf = true;
×
1751
                    self.load_time_zone_essentials(zone_essentials_provider, locale)?;
×
1752
                    self.load_time_zone_location_names(locations_provider, locale)?;
×
1753
                }
1754

1755
                // O, OOOO
1756
                (FS::TimeZone(TimeZone::LocalizedOffset), One | Four) => {
×
1757
                    self.load_time_zone_essentials(zone_essentials_provider, locale)?;
×
1758
                    load_fdf = true;
×
1759
                }
1760

1761
                // X..XXXXX, x..xxxxx
1762
                (
1763
                    FS::TimeZone(TimeZone::IsoWithZ | TimeZone::Iso),
1764
                    One | Two | Three | Four | Five,
1765
                ) => {
1766
                    // no data required
1767
                }
1768

1769
                ///// Numeric symbols /////
1770

1771
                // y+
1772
                (FS::Year(Year::Calendar), _) => load_fdf = true,
×
1773
                // r+
1774
                (FS::Year(Year::RelatedIso), _) => {
1775
                    // always formats as ASCII
1776
                }
1777

1778
                // M..MM, L..LL
1779
                (FS::Month(_), One | Two) => load_fdf = true,
×
1780

1781
                // e..ee, c..cc
1782
                (FS::Weekday(Weekday::Local | Weekday::StandAlone), One | Two) => {
1783
                    // TODO(#5643): Requires locale-aware day-of-week calculation
1784
                    return Err(PatternLoadError::UnsupportedLength(field));
×
1785
                }
1786

1787
                // d..dd
1788
                (FS::Day(Day::DayOfMonth), One | Two) => load_fdf = true,
×
1789
                // D..DDD
1790
                (FS::Day(Day::DayOfYear), One | Two | Three) => load_fdf = true,
×
1791
                // F
1792
                (FS::Day(Day::DayOfWeekInMonth), One) => load_fdf = true,
×
1793

1794
                // K..KK, h..hh, H..HH, k..kk
1795
                (FS::Hour(_), One | Two) => load_fdf = true,
×
1796

1797
                // m..mm
1798
                (FS::Minute, One | Two) => load_fdf = true,
×
1799

1800
                // s..ss
1801
                (FS::Second(Second::Second), One | Two) => load_fdf = true,
×
1802

1803
                // A+
1804
                (FS::Second(Second::MillisInDay), _) => load_fdf = true,
×
1805

1806
                // s.S+, ss.S+ (s is modelled by length, S+ by symbol)
1807
                (FS::DecimalSecond(_), One | Two) => load_fdf = true,
×
1808

1809
                ///// Unsupported symbols /////
1810
                _ => {
1811
                    return Err(PatternLoadError::UnsupportedLength(field));
×
1812
                }
1813
            }
1814
        }
×
1815

1816
        if load_fdf {
×
1817
            self.load_fixed_decimal_formatter(fixed_decimal_formatter_loader, locale)
×
1818
                .map_err(PatternLoadError::Data)?;
×
1819
        }
1820

1821
        Ok(())
×
1822
    }
×
1823
}
1824

1825
impl<'data> RawDateTimeNamesBorrowed<'data> {
1826
    pub(crate) fn get_name_for_month(
472✔
1827
        &self,
1828
        field_symbol: fields::Month,
1829
        field_length: FieldLength,
1830
        code: MonthCode,
1831
    ) -> Result<MonthPlaceholderValue, GetNameForMonthError> {
1832
        let month_names = self
944✔
1833
            .month_names
1834
            .get_with_variables((field_symbol, field_length))
1835
            .ok_or(GetNameForMonthError::NotLoaded)?;
472✔
1836
        let Some((month_number, is_leap)) = code.parsed() else {
470✔
1837
            return Err(GetNameForMonthError::Invalid);
×
1838
        };
1839
        let Some(month_index) = month_number.checked_sub(1) else {
470✔
1840
            return Err(GetNameForMonthError::Invalid);
×
1841
        };
1842
        let month_index = usize::from(month_index);
470✔
1843
        let name = match month_names {
470✔
1844
            MonthNamesV1::Linear(linear) => {
394✔
1845
                if is_leap {
394✔
1846
                    None
×
1847
                } else {
1848
                    linear.get(month_index)
394✔
1849
                }
1850
            }
1851
            MonthNamesV1::LeapLinear(leap_linear) => {
76✔
1852
                let num_months = leap_linear.len() / 2;
76✔
1853
                if is_leap {
76✔
1854
                    leap_linear.get(month_index + num_months)
26✔
1855
                } else if month_index < num_months {
50✔
1856
                    leap_linear.get(month_index)
50✔
1857
                } else {
1858
                    None
×
1859
                }
1860
            }
1861
            MonthNamesV1::LeapNumeric(leap_numeric) => {
×
1862
                if is_leap {
×
1863
                    return Ok(MonthPlaceholderValue::NumericPattern(leap_numeric));
×
1864
                } else {
1865
                    return Ok(MonthPlaceholderValue::Numeric);
×
1866
                }
1867
            }
1868
        };
1869
        // Note: Always return `false` for the second argument since neo MonthNames
1870
        // knows how to handle leap months and we don't need the fallback logic
1871
        name.map(MonthPlaceholderValue::PlainString)
940✔
1872
            .ok_or(GetNameForMonthError::Invalid)
470✔
1873
    }
472✔
1874

1875
    pub(crate) fn get_name_for_weekday(
400✔
1876
        &self,
1877
        field_symbol: fields::Weekday,
1878
        field_length: FieldLength,
1879
        day: input::IsoWeekday,
1880
    ) -> Result<&str, GetNameForWeekdayError> {
1881
        // UTS 35 says that "e" and "E" have the same non-numeric names
1882
        let field_symbol = field_symbol.to_format_symbol();
400✔
1883
        // UTS 35 says that "E..EEE" are all Abbreviated
1884
        // However, this doesn't apply to "e" and "c".
1885
        let field_length = if matches!(field_symbol, fields::Weekday::Format) {
400✔
1886
            field_length.numeric_to_abbr()
358✔
1887
        } else {
1888
            field_length
42✔
1889
        };
1890
        let weekday_names = self
800✔
1891
            .weekday_names
1892
            .get_with_variables((field_symbol, field_length))
400✔
1893
            .ok_or(GetNameForWeekdayError::NotLoaded)?;
400✔
1894
        weekday_names
796✔
1895
            .names
1896
            .get((day as usize) % 7)
398✔
1897
            // TODO: make weekday_names length 7 in the type system
1898
            .ok_or(GetNameForWeekdayError::NotLoaded)
1899
    }
400✔
1900

1901
    /// Gets the era symbol, or `None` if data is loaded but symbol isn't found.
1902
    ///
1903
    /// `None` should fall back to the era code directly, if, for example,
1904
    /// a japanext datetime is formatted with a `DateTimeFormat<Japanese>`
1905
    pub(crate) fn get_name_for_era(
312✔
1906
        &self,
1907
        field_length: FieldLength,
1908
        era: FormattingEra,
1909
    ) -> Result<&str, GetSymbolForEraError> {
1910
        // UTS 35 says that "G..GGG" are all Abbreviated
1911
        let field_length = field_length.numeric_to_abbr();
312✔
1912
        let year_names = self
624✔
1913
            .year_names
1914
            .get_with_variables(field_length)
1915
            .ok_or(GetSymbolForEraError::NotLoaded)?;
312✔
1916

1917
        match (year_names, era) {
310✔
1918
            (YearNamesV1::VariableEras(era_names), FormattingEra::Code(era_code)) => era_names
36✔
1919
                .get(era_code.0.as_str().into())
12✔
1920
                .ok_or(GetSymbolForEraError::Invalid),
12✔
1921
            (YearNamesV1::FixedEras(era_names), FormattingEra::Index(index, _fallback)) => {
298✔
1922
                era_names
894✔
1923
                    .get(index.into())
298✔
1924
                    .ok_or(GetSymbolForEraError::Invalid)
298✔
1925
            }
1926
            _ => Err(GetSymbolForEraError::Invalid),
×
1927
        }
1928
    }
312✔
1929

1930
    pub(crate) fn get_name_for_cyclic(
42✔
1931
        &self,
1932
        field_length: FieldLength,
1933
        cyclic: NonZeroU8,
1934
    ) -> Result<&str, GetSymbolForCyclicYearError> {
1935
        // UTS 35 says that "U..UUU" are all Abbreviated
1936
        let field_length = field_length.numeric_to_abbr();
42✔
1937
        let year_names = self
84✔
1938
            .year_names
1939
            .get_with_variables(field_length)
1940
            .ok_or(GetSymbolForCyclicYearError::NotLoaded)?;
42✔
1941

1942
        let YearNamesV1::Cyclic(cyclics) = year_names else {
42✔
1943
            return Err(GetSymbolForCyclicYearError::Invalid { max: 0 });
×
1944
        };
1945

1946
        cyclics
126✔
1947
            .get((cyclic.get() as usize) - 1)
42✔
1948
            .ok_or(GetSymbolForCyclicYearError::Invalid {
42✔
1949
                max: cyclics.len() + 1,
42✔
1950
            })
1951
    }
42✔
1952

1953
    pub(crate) fn get_name_for_day_period(
511✔
1954
        &self,
1955
        field_symbol: fields::DayPeriod,
1956
        field_length: FieldLength,
1957
        hour: input::IsoHour,
1958
        is_top_of_hour: bool,
1959
    ) -> Result<&str, GetNameForDayPeriodError> {
1960
        use fields::DayPeriod::NoonMidnight;
1961
        // UTS 35 says that "a..aaa" are all Abbreviated
1962
        let field_length = field_length.numeric_to_abbr();
511✔
1963
        let dayperiod_names = self
511✔
1964
            .dayperiod_names
1965
            .get_with_variables(field_length)
1966
            .ok_or(GetNameForDayPeriodError::NotLoaded)?;
511✔
1967
        let option_value: Option<&str> = match (field_symbol, u8::from(hour), is_top_of_hour) {
509✔
1968
            (NoonMidnight, 00, true) => dayperiod_names.midnight().or_else(|| dayperiod_names.am()),
64✔
1969
            (NoonMidnight, 12, true) => dayperiod_names.noon().or_else(|| dayperiod_names.pm()),
74✔
1970
            (_, hour, _) if hour < 12 => dayperiod_names.am(),
391✔
1971
            _ => dayperiod_names.pm(),
197✔
1972
        };
1973
        option_value.ok_or(GetNameForDayPeriodError::NotLoaded)
509✔
1974
    }
511✔
1975
}
1976

1977
/// A container contains all data payloads for time zone formatting (borrowed version).
1978
#[derive(Debug, Copy, Clone, Default)]
×
1979
pub(crate) struct TimeZoneDataPayloadsBorrowed<'a> {
1980
    /// The data that contains meta information about how to display content.
1981
    pub(crate) essentials: Option<&'a tz::EssentialsV1<'a>>,
×
1982
    /// The root location names, e.g. Toronto
1983
    pub(crate) locations_root: Option<&'a tz::LocationsV1<'a>>,
×
1984
    /// The language specific location names, e.g. Italy
1985
    pub(crate) locations: Option<&'a tz::LocationsV1<'a>>,
×
1986
    /// The generic long metazone names, e.g. Pacific Time
1987
    pub(crate) mz_generic_long: Option<&'a tz::MzGenericV1<'a>>,
×
1988
    /// The generic short metazone names, e.g. PT
1989
    pub(crate) mz_generic_short: Option<&'a tz::MzGenericV1<'a>>,
×
1990
    /// The specific long metazone names, e.g. Pacific Daylight Time
1991
    pub(crate) mz_specific_long: Option<&'a tz::MzSpecificV1<'a>>,
×
1992
    /// The specific short metazone names, e.g. Pacific Daylight Time
1993
    pub(crate) mz_specific_short: Option<&'a tz::MzSpecificV1<'a>>,
×
1994
    /// The metazone lookup
1995
    pub(crate) mz_periods: Option<&'a tz::MzPeriodV1<'a>>,
×
1996
}
1997

1998
impl<'data> RawDateTimeNamesBorrowed<'data> {
1999
    pub(crate) fn get_payloads(&self) -> TimeZoneDataPayloadsBorrowed<'data> {
691✔
2000
        TimeZoneDataPayloadsBorrowed {
691✔
2001
            essentials: self.zone_essentials.get_option(),
691✔
2002
            locations_root: self.locations_root.get_option(),
691✔
2003
            locations: self.locations.get_option(),
691✔
2004
            mz_generic_long: self.mz_generic_long.get_option(),
691✔
2005
            mz_generic_short: self.mz_generic_short.get_option(),
691✔
2006
            mz_specific_long: self.mz_specific_long.get_option(),
691✔
2007
            mz_specific_short: self.mz_specific_short.get_option(),
691✔
2008
            mz_periods: self.mz_periods.get_option(),
691✔
2009
        }
2010
    }
691✔
2011
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc