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

zbraniecki / icu4x / 12020603084

23 Nov 2024 08:43PM UTC coverage: 75.71% (+0.2%) from 75.477%
12020603084

push

github

sffc
Touch Cargo.lock

55589 of 73424 relevant lines covered (75.71%)

644270.14 hits per line

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

62.5
/components/datetime/src/dynamic.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
//! Enumerations over [field sets](crate::fieldsets).
6
//!
7
//! These enumerations can be used when the field set is not known at compile time. However,
8
//! they may contribute negatively to the binary size of the formatters.
9
//!
10
//! The most general type is [`CompositeFieldSet`], which supports all field
11
//! sets in a single enumeration. [`CompositeDateTimeFieldSet`] is a good
12
//! choice when you don't need to format time zones.
13
//!
14
//! Summary of all the types:
15
//!
16
//! | Type | Supported Field Sets |
17
//! |---|---|
18
//! | [`DateFieldSet`] | Date |
19
//! | [`CalendarPeriodFieldSet`] | Calendar Period |
20
//! | [`TimeFieldSet`] | Time |
21
//! | [`ZoneFieldSet`] | Zone |
22
//! | [`DateAndTimeFieldSet`] | Date + Time |
23
//! | [`CompositeDateTimeFieldSet`] | Date, Calendar Period, Time, Date + Time |
24
//! | [`CompositeFieldSet`] | All |
25
//!
26
//! # Examples
27
//!
28
//! Format with the time display depending on a runtime boolean:
29
//!
30
//! ```
31
//! use icu::calendar::DateTime;
32
//! use icu::datetime::fieldsets;
33
//! use icu::datetime::fieldsets::enums::CompositeDateTimeFieldSet;
34
//! use icu::datetime::DateTimeFormatter;
35
//! use icu::locale::locale;
36
//! use writeable::Writeable;
37
//!
38
//! fn get_field_set(should_display_time: bool) -> CompositeDateTimeFieldSet {
39
//!     if should_display_time {
40
//!         let field_set = fieldsets::MDT::medium().hm();
41
//!         CompositeDateTimeFieldSet::DateTime(
42
//!             fieldsets::enums::DateAndTimeFieldSet::MDT(field_set),
43
//!         )
44
//!     } else {
45
//!         let field_set = fieldsets::MD::medium();
46
//!         CompositeDateTimeFieldSet::Date(fieldsets::enums::DateFieldSet::MD(
47
//!             field_set,
48
//!         ))
49
//!     }
50
//! }
51
//!
52
//! let datetime = DateTime::try_new_iso(2025, 1, 15, 16, 0, 0).unwrap();
53
//!
54
//! let results = [true, false]
55
//!     .map(get_field_set)
56
//!     .map(|field_set| {
57
//!         DateTimeFormatter::try_new(locale!("en-US").into(), field_set)
58
//!             .unwrap()
59
//!     })
60
//!     .map(|formatter| formatter.format_any_calendar(&datetime).to_string());
61
//!
62
//! assert_eq!(results, ["Jan 15, 4:00 PM", "Jan 15"])
63
//! ```
64

65
use crate::raw::neo::RawOptions;
66
use crate::scaffold::GetField;
67
use crate::{fields, fieldsets, Length};
68
use icu_provider::prelude::*;
69

70
/// An enumeration over all possible date field sets.
71
///
72
/// This is a dynamic field set. For more information, see [`enums`](crate::fieldsets::enums).
73
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
2✔
74
#[non_exhaustive]
75
pub enum DateFieldSet {
76
    /// The day of the month, as in
77
    /// “on the 1st”.
78
    D(fieldsets::D),
×
79
    /// The month and day of the month, as in
80
    /// “January 1st”.
81
    MD(fieldsets::MD),
×
82
    /// The year, month, and day of the month, as in
83
    /// “January 1st, 2000”.
84
    YMD(fieldsets::YMD),
1✔
85
    /// The day of the month and day of the week, as in
86
    /// “Saturday 1st”.
87
    DE(fieldsets::DE),
×
88
    /// The month, day of the month, and day of the week, as in
89
    /// “Saturday, January 1st”.
90
    MDE(fieldsets::MDE),
×
91
    /// The year, month, day of the month, and day of the week, as in
92
    /// “Saturday, January 1st, 2000”.
93
    YMDE(fieldsets::YMDE),
×
94
    /// The day of the week alone, as in
95
    /// “Saturday”.
96
    E(fieldsets::E),
×
97
}
98

99
/// An enumeration over all possible calendar period field sets.
100
///
101
/// This is a dynamic field set. For more information, see [`enums`](crate::fieldsets::enums).
102
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
×
103
#[non_exhaustive]
104
pub enum CalendarPeriodFieldSet {
105
    /// A standalone month, as in
106
    /// “January”.
107
    M(fieldsets::M),
×
108
    /// A month and year, as in
109
    /// “January 2000”.
110
    YM(fieldsets::YM),
×
111
    /// A year, as in
112
    /// “2000”.
113
    Y(fieldsets::Y),
×
114
    // TODO: Add support for week-of-year
115
    // /// The year and week of the year, as in
116
    // /// “52nd week of 1999”.
117
    // YW(fieldsets::YW),
118
    // TODO(#501): Consider adding support for Quarter and YearQuarter.
119
}
120

121
/// An enumeration over all possible time field sets.
122
///
123
/// This is a dynamic field set. For more information, see [`enums`](crate::fieldsets::enums).
124
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
×
125
#[non_exhaustive]
126
pub enum TimeFieldSet {
127
    /// A time of day.
128
    T(fieldsets::T),
×
129
}
130

131
/// An enumeration over all possible zone field sets.
132
///
133
/// This is a dynamic field set. For more information, see [`enums`](crate::fieldsets::enums).
134
///
135
/// Note: [`fieldsets::Zs`] and [`fieldsets::Vs`] are not included in this enum
136
/// because they are data size optimizations only.
137
///
138
/// # Time Zone Data Size
139
///
140
/// Time zone names contribute a lot of data size. For resource-constrained
141
/// environments, the following formats require the least amount of data:
142
///
143
/// - [`fieldsets::Zs`]
144
/// - [`fieldsets::O`]
145
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
×
146
#[non_exhaustive]
147
pub enum ZoneFieldSet {
148
    /// The specific non-location format, as in
149
    /// “Pacific Daylight Time”.
150
    Z(fieldsets::Z),
×
151
    /// The offset format, as in
152
    /// “GMT−8”.
153
    O(fieldsets::O),
×
154
    /// The generic non-location format, as in
155
    /// “Pacific Time”.
156
    V(fieldsets::V),
×
157
    /// The location format, as in
158
    /// “Los Angeles time”.
159
    L(fieldsets::L),
×
160
}
161

162
/// An enumeration over all possible zone styles.
163
///
164
/// This is similar to [`ZoneFieldSet`], except the fields are not
165
/// self-contained semantic skeletons: they do not contain the length.
166
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
2✔
167
#[non_exhaustive]
168
pub enum ZoneStyle {
169
    /// The specific non-location format, as in
170
    /// “Pacific Daylight Time”.
171
    Z,
172
    /// The offset format, as in
173
    /// “GMT−8”.
174
    O,
175
    /// The generic non-location format, as in
176
    /// “Pacific Time”.
177
    V,
178
    /// The location format, as in
179
    /// “Los Angeles time”.
180
    L,
181
}
182

183
/// An enumeration over all possible date+time composite field sets.
184
///
185
/// This is a dynamic field set. For more information, see [`enums`](crate::fieldsets::enums).
186
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
4✔
187
#[non_exhaustive]
188
pub enum DateAndTimeFieldSet {
189
    /// The day of the month with time of day, as in
190
    /// “on the 1st at 10:31 AM”.
191
    DT(fieldsets::DT),
×
192
    /// The month and day of the month with time of day, as in
193
    /// “January 1st at 10:31 AM”.
194
    MDT(fieldsets::MDT),
×
195
    /// The year, month, and day of the month with time of day, as in
196
    /// “January 1st, 2000 at 10:31 AM”.
197
    YMDT(fieldsets::YMDT),
×
198
    /// The day of the month and day of the week with time of day, as in
199
    /// “Saturday 1st at 10:31 AM”.
200
    DET(fieldsets::DET),
×
201
    /// The month, day of the month, and day of the week with time of day, as in
202
    /// “Saturday, January 1st at 10:31 AM”.
203
    MDET(fieldsets::MDET),
×
204
    /// The year, month, day of the month, and day of the week with time of day, as in
205
    /// “Saturday, January 1st, 2000 at 10:31 AM”.
206
    YMDET(fieldsets::YMDET),
2✔
207
    /// The day of the week alone with time of day, as in
208
    /// “Saturday at 10:31 AM”.
209
    ET(fieldsets::ET),
×
210
}
211

212
/// An enum supporting date, calendar period, time, and date+time field sets
213
/// and options.
214
///
215
/// Time zones are not supported with this enum.
216
///
217
/// This enum is useful when formatting a type that does not contain a
218
/// time zone or to avoid storing time zone data.
219
///
220
/// This is a dynamic field set. For more information, see [`enums`](crate::fieldsets::enums).
221
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
×
222
#[non_exhaustive]
223
pub enum CompositeDateTimeFieldSet {
224
    /// Field set for a date.
225
    Date(DateFieldSet),
×
226
    /// Field set for a calendar period.
227
    CalendarPeriod(CalendarPeriodFieldSet),
×
228
    /// Field set for a time.
229
    Time(TimeFieldSet),
×
230
    /// Field set for a date and a time together.
231
    DateTime(DateAndTimeFieldSet),
×
232
}
233

234
impl CompositeDateTimeFieldSet {
235
    /// If the [`CompositeFieldSet`] does not contain a time zone,
236
    /// returns the corresponding [`CompositeDateTimeFieldSet`].
237
    pub fn try_from_composite_field_set(field_set: CompositeFieldSet) -> Option<Self> {
53✔
238
        match field_set {
53✔
239
            CompositeFieldSet::Date(v) => Some(Self::Date(v)),
16✔
240
            CompositeFieldSet::CalendarPeriod(v) => Some(Self::CalendarPeriod(v)),
8✔
241
            CompositeFieldSet::Time(v) => Some(Self::Time(v)),
20✔
242
            CompositeFieldSet::Zone(_) => None,
×
243
            CompositeFieldSet::DateTime(v) => Some(Self::DateTime(v)),
9✔
244
            CompositeFieldSet::DateZone(_, _) => None,
×
245
            CompositeFieldSet::TimeZone(_, _) => None,
×
246
            CompositeFieldSet::DateTimeZone(_, _) => None,
×
247
        }
248
    }
53✔
249

250
    /// Returns the [`CompositeFieldSet`] corresponding to this
251
    /// [`CompositeDateTimeFieldSet`].
252
    pub fn to_composite_field_set(self) -> CompositeFieldSet {
273✔
253
        match self {
273✔
254
            Self::Date(v) => CompositeFieldSet::Date(v),
83✔
255
            Self::CalendarPeriod(v) => CompositeFieldSet::CalendarPeriod(v),
28✔
256
            Self::Time(v) => CompositeFieldSet::Time(v),
42✔
257
            Self::DateTime(v) => CompositeFieldSet::DateTime(v),
120✔
258
        }
259
    }
273✔
260
}
261

262
impl GetField<CompositeFieldSet> for CompositeDateTimeFieldSet {
263
    fn get_field(&self) -> CompositeFieldSet {
271✔
264
        self.to_composite_field_set()
271✔
265
    }
271✔
266
}
267

268
/// An enum supporting all possible field sets and options.
269
///
270
/// This is a dynamic field set. For more information, see [`enums`](crate::fieldsets::enums).
271
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
4✔
272
#[non_exhaustive]
273
pub enum CompositeFieldSet {
274
    /// Field set for a date.
275
    Date(DateFieldSet),
17✔
276
    /// Field set for a calendar period.
277
    CalendarPeriod(CalendarPeriodFieldSet),
8✔
278
    /// Field set for a time.
279
    Time(TimeFieldSet),
20✔
280
    /// Field set for a time zone.
281
    Zone(ZoneFieldSet),
×
282
    /// Field set for a date and a time together.
283
    DateTime(DateAndTimeFieldSet),
×
284
    /// Field set for a date and a time zone together.
285
    DateZone(DateFieldSet, ZoneStyle),
×
286
    /// Field set for a time and a time zone together.
287
    TimeZone(TimeFieldSet, ZoneStyle),
×
288
    /// Field set for a date, a time, and a time zone together.
289
    DateTimeZone(DateAndTimeFieldSet, ZoneStyle),
2✔
290
}
291

292
macro_rules! first {
293
    ($first:literal, $($remainder:literal,)*) => {
294
        $first
295
    };
296
}
297

298
macro_rules! impl_attrs {
299
    (@attrs, $type:path, [$(($attr_var:ident, $str_var:ident, $value:literal),)+]) => {
300
        impl $type {
301
            $(
302
                const $attr_var: &'static DataMarkerAttributes = DataMarkerAttributes::from_str_or_panic($value);
303
            )+
304
            /// All attributes associated with this enum.
305
            ///
306
            /// # Encoding Details
307
            ///
308
            /// The string is based roughly on the UTS 35 symbol table with the following exceptions:
309
            ///
310
            /// 1. Lowercase letters are chosen where there is no ambiguity: `E` becomes `e`
311
            /// 2. Capitals are replaced with their lowercase and a number 0: `M` becomes `m0`
312
            /// 3. A single symbol is included for each component: length doesn't matter
313
            /// 4. Time fields are encoded with their hour field only: `j`, `h`, or `h0`
314
            ///
315
            /// # Examples
316
            ///
317
            /// ```
318
            #[doc = concat!("use icu::datetime::fieldsets::enums::", stringify!($type), " as FS;")]
319
            /// use icu_provider::DataMarkerAttributes;
320
            ///
321
            /// assert!(FS::ALL_DATA_MARKER_ATTRIBUTES.contains(
322
            #[doc = concat!("    &DataMarkerAttributes::from_str_or_panic(\"", first!($($value,)*), "\")")]
323
            /// ));
324
            /// ```
325
            pub const ALL_DATA_MARKER_ATTRIBUTES: &'static [&'static DataMarkerAttributes] = &[
326
                $(
327
                    Self::$attr_var,
328
                )+
329
            ];
330
        }
331
    };
332
    (@id_str, $type:path, [$(($variant:ident, $attr_var:ident)),+,]) => {
333
        impl $type {
334
            /// Returns a stable string identifying this set of fields.
335
            pub(crate) const fn id_str(self) -> &'static DataMarkerAttributes {
293✔
336
                match self {
293✔
337
                    $(
338
                        Self::$variant(_) => Self::$attr_var,
293✔
339
                    )+
340
                }
341
            }
293✔
342
        }
343
    };
344
    (@to_raw_options, $type:path, [$($variant:ident),+,]) => {
345
        impl $type {
346
            pub(crate) fn to_raw_options(self) -> RawOptions {
358✔
347
                match self {
302✔
348
                    $(
349
                        Self::$variant(variant) => variant.to_raw_options(),
358✔
350
                    )+
351
                }
352
            }
358✔
353
        }
354
    };
355
    (@composite, $type:path, $variant:ident) => {
356
        impl $type {
357
            #[inline]
358
            pub(crate) fn to_enum(self) -> $type {
×
359
                self
×
360
            }
×
361
        }
362
        impl GetField<CompositeFieldSet> for $type {
363
            #[inline]
364
            fn get_field(&self) -> CompositeFieldSet {
×
365
                CompositeFieldSet::$variant(self.to_enum())
×
366
            }
×
367
        }
368
    };
369
    (@date, $type:path, [$(($variant:ident, $attr_var:ident, $str_var:ident, $value:literal)),+,]) => {
370
        impl_attrs! { @attrs, $type, [$(($attr_var, $str_var, $value)),+,] }
371
        impl_attrs! { @id_str, $type, [$(($variant, $attr_var)),+,] }
372
        impl_attrs! { @to_raw_options, $type, [$($variant),+,] }
373
        impl_attrs! { @composite, $type, Date }
374
    };
375
    (@calendar_period, $type:path, [$(($variant:ident, $attr_var:ident, $str_var:ident, $value:literal)),+,]) => {
376
        impl_attrs! { @attrs, $type, [$(($attr_var, $str_var, $value)),+,] }
377
        impl_attrs! { @to_raw_options, $type, [$($variant),+,] }
378
        impl_attrs! { @composite, $type, CalendarPeriod }
379
        impl_attrs! { @id_str, $type, [$(($variant, $attr_var)),+,] }
380
    };
381
    (@time, $type:path, [$(($attr_var:ident, $str_var:ident, $value:literal)),+,]) => {
382
        impl_attrs! { @attrs, $type, [$(($attr_var, $str_var, $value)),+,] }
383
        impl_attrs! { @to_raw_options, $type, [T,] }
384
        impl_attrs! { @composite, $type, Time }
385
    };
386
    (@zone, $type:path, [$($variant:ident),+,]) => {
387
        impl_attrs! { @composite, $type, Zone }
388
        impl $type {
389
            pub(crate) fn to_field(self) -> (fields::TimeZone, fields::FieldLength) {
111✔
390
                match self {
111✔
391
                    $(
392
                        Self::$variant(variant) => variant.to_field(),
111✔
393
                    )+
394
                }
395
            }
111✔
396
        }
397
    };
398
    (@datetime, $type:path, [$(($d_variant:ident, $variant:ident)),+,]) => {
399
        impl_attrs! { @to_raw_options, $type, [$($variant),+,] }
400
        impl_attrs! { @composite, $type, DateTime }
401
        impl $type {
402
            pub(crate) fn to_date_field_set(self) -> DateFieldSet {
142✔
403
                match self {
142✔
404
                    $(
405
                        Self::$variant(variant) => DateFieldSet::$d_variant(variant.to_date_field_set()),
142✔
406
                    )+
407
                }
408
            }
142✔
409
            pub(crate) fn to_time_field_set(self) -> TimeFieldSet {
142✔
410
                let (length, time_precision, alignment) = match self {
284✔
411
                    $(
412
                        Self::$variant(variant) => (variant.length, variant.time_precision, variant.alignment),
142✔
413
                    )+
414
                };
415
                TimeFieldSet::T(fieldsets::T {
142✔
416
                    length,
417
                    time_precision,
418
                    alignment,
419
                })
420
            }
142✔
421
            #[cfg(all(feature = "serde", feature = "experimental"))]
422
            pub(crate) fn from_date_field_set_with_raw_options(date_field_set: DateFieldSet, options: RawOptions) -> Self {
16✔
423
                match date_field_set {
16✔
424
                    $(
425
                        DateFieldSet::$d_variant(_) => Self::$variant(fieldsets::$variant::from_raw_options(options)),
16✔
426
                    )+
427
                }
428
            }
16✔
429
        }
430
    };
431
}
432

433
impl_attrs! {
434
    @date,
435
    DateFieldSet,
436
    [
437
        (D, ATTR_D, STR_D, "d"),
438
        (MD, ATTR_MD, STR_MD, "m0d"),
439
        (YMD, ATTR_YMD, STR_YMD, "ym0d"),
440
        (DE, ATTR_DE, STR_DE, "de"),
441
        (MDE, ATTR_MDE, STR_MDE, "m0de"),
442
        (YMDE, ATTR_YMDE, STR_YMDE, "ym0de"),
443
        (E, ATTR_E, STR_E, "e"),
444
    ]
445
}
446

447
impl_attrs! {
448
    @calendar_period,
449
    CalendarPeriodFieldSet,
450
    [
451
        (M, ATTR_M, STR_M, "m0"),
452
        (YM, ATTR_YM, STR_YM, "ym0"),
453
        (Y, ATTR_Y, STR_Y, "y"),
454
    ]
455
}
456

457
impl_attrs! {
458
    @time,
459
    TimeFieldSet,
460
    [
461
        (ATTR_T, STR_T, "j"),
462
        (ATTR_T12, STR_T12, "h"),
463
        (ATTR_T24, STR_T24, "h0"),
464
    ]
465
}
466

467
impl TimeFieldSet {
468
    pub(crate) const fn id_str_for_hour_cycle(
197✔
469
        self,
470
        hour_cycle: Option<fields::Hour>,
471
    ) -> &'static DataMarkerAttributes {
472
        use fields::Hour::*;
473
        match hour_cycle {
197✔
474
            None => Self::ATTR_T,
65✔
475
            Some(H11 | H12) => Self::ATTR_T12,
27✔
476
            Some(H23 | H24) => Self::ATTR_T24,
105✔
477
        }
478
    }
197✔
479
}
480

481
impl_attrs! {
482
    @zone,
483
    ZoneFieldSet,
484
    [
485
        Z,
486
        O,
487
        V,
488
        L,
489
    ]
490
}
491

492
impl ZoneFieldSet {
493
    pub(crate) fn from_time_zone_style_and_length(style: ZoneStyle, length: Length) -> Self {
14✔
494
        match style {
14✔
495
            ZoneStyle::Z => Self::Z(fieldsets::Z::with_length(length)),
4✔
496
            ZoneStyle::O => Self::O(fieldsets::O::with_length(length)),
3✔
497
            ZoneStyle::V => Self::V(fieldsets::V::with_length(length)),
5✔
498
            ZoneStyle::L => Self::L(fieldsets::L::with_length(length)),
2✔
499
        }
500
    }
14✔
501
}
502

503
impl_attrs! {
504
    @attrs,
505
    DateAndTimeFieldSet,
506
    [
507
        (ATTR_ET, STR_ET, "ej"),
508
    ]
509
}
510

511
impl_attrs! {
512
    @datetime,
513
    DateAndTimeFieldSet,
514
    [
515
        (D, DT),
516
        (MD, MDT),
517
        (YMD, YMDT),
518
        (DE, DET),
519
        (MDE, MDET),
520
        (YMDE, YMDET),
521
        (E, ET),
522
    ]
523
}
524

525
impl DateAndTimeFieldSet {
526
    pub(crate) const fn id_str(self) -> Option<&'static DataMarkerAttributes> {
137✔
527
        match self {
137✔
528
            DateAndTimeFieldSet::ET(_) => Some(Self::ATTR_ET),
15✔
529
            _ => None,
122✔
530
        }
531
    }
137✔
532
}
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