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

zbraniecki / icu4x / 13958601093

19 Mar 2025 04:17PM UTC coverage: 74.164% (-1.5%) from 75.71%
13958601093

push

github

web-flow
Clean up properties docs (#6315)

58056 of 78281 relevant lines covered (74.16%)

819371.32 hits per line

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

89.41
/components/datetime/src/fieldsets.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
//! All available field sets for datetime formatting.
6
//!
7
//! Each field set is a struct containing options specified to that field set.
8
//! The fields can either be set directly or via helper functions.
9
//!
10
//! This module contains _static_ field sets, which deliver the smallest binary size.
11
//! If the field set is not known until runtime, use a _dynamic_ field set: [`enums`]
12
//!
13
//! # What is a Field Set?
14
//!
15
//! A field set determines what datetime fields should be printed in the localized output.
16
//!
17
//! Examples of field sets include:
18
//!
19
//! 1. Year, month, and day ([`YMD`])
20
//! 2. Weekday and time ([`ET`])
21
//!
22
//! Field sets fit into four categories:
23
//!
24
//! 1. Date: fields that specify a particular day in time.
25
//! 2. Calendar period: fields that specify a span of time greater than a day.
26
//! 3. Time: fields that specify a time within a day.
27
//! 4. Zone: fields that specify a time zone or offset from UTC.
28
//!
29
//! Certain combinations of field sets are allowed, too. See [`Combo`].
30
//!
31
//! # Examples
32
//!
33
//! Two ways to configure the same field set:
34
//!
35
//! ```
36
//! use icu::datetime::fieldsets::YMDT;
37
//! use icu::datetime::options::{Alignment, TimePrecision, YearStyle};
38
//!
39
//! let field_set_1 = YMDT::long()
40
//!     .with_year_style(YearStyle::Full)
41
//!     .with_alignment(Alignment::Column)
42
//!     .with_time_precision(TimePrecision::Minute);
43
//!
44
//! let mut field_set_2 = YMDT::long();
45
//! field_set_2.year_style = Some(YearStyle::Full);
46
//! field_set_2.alignment = Some(Alignment::Column);
47
//! field_set_2.time_precision = Some(TimePrecision::Minute);
48
//!
49
//! assert_eq!(field_set_1, field_set_2);
50
//! ```
51

52
#[path = "builder.rs"]
53
pub mod builder;
54
#[path = "dynamic.rs"]
55
pub mod enums;
56

57
pub use crate::combo::Combo;
58

59
use crate::{
60
    options::*,
61
    provider::{neo::*, time_zones::tz, *},
62
    raw::neo::RawOptions,
63
    scaffold::*,
64
};
65
use enums::*;
66
use icu_calendar::{
67
    types::{DayOfMonth, MonthInfo, Weekday, YearInfo},
68
    Date, Iso,
69
};
70
use icu_provider::marker::NeverMarker;
71
use icu_time::{
72
    zone::{TimeZoneVariant, UtcOffset},
73
    Hour, Minute, Nanosecond, Second, Time, TimeZone,
74
};
75

76
#[cfg(doc)]
77
use icu_time::TimeZoneInfo;
78

79
/// Maps the token `yes` to the given ident
80
macro_rules! yes_to {
81
    ($any:expr, $nonempty:expr) => {
82
        $any
83
    };
84
}
85

86
macro_rules! yes_or {
87
    ($fallback:expr, $actual:expr) => {
88
        $actual
89
    };
90
    ($fallback:expr,) => {
91
        $fallback
92
    };
93
}
94

95
macro_rules! ternary {
96
    ($present:expr, $missing:expr, yes) => {
97
        $present
98
    };
99
    ($present:expr, $missing:expr, $any:literal) => {
100
        $present
101
    };
102
    ($present:expr, $missing:expr,) => {
103
        $missing
104
    };
105
}
106

107
/// Generates the options argument passed into the docs test constructor
108
macro_rules! length_option_helper {
109
    ($type:ty, $length:ident) => {
110
        concat!(stringify!($type), "::", stringify!($length), "()")
111
    };
112
}
113

114
macro_rules! impl_composite {
115
    ($type:ident, $variant:ident, $enum:ident) => {
116
        impl $type {
117
            #[inline]
118
            pub(crate) fn to_enum(self) -> $enum {
2✔
119
                $enum::$type(self)
2✔
120
            }
2✔
121
        }
122
        impl GetField<CompositeFieldSet> for $type {
123
            #[inline]
124
            fn get_field(&self) -> CompositeFieldSet {
×
125
                CompositeFieldSet::$variant(self.to_enum())
×
126
            }
×
127
        }
128
    };
129
}
130

131
macro_rules! impl_marker_length_constructors {
132
    (
133
        $type:ident,
134
        $(alignment: $alignment_yes:ident,)?
135
        $(year_style: $yearstyle_yes:ident,)?
136
        $(time_precision: $timeprecision_yes:ident,)?
137
    ) => {
138
        impl $type {
139
            #[doc = concat!("Creates a ", stringify!($type), " skeleton with the given formatting length.")]
140
            pub const fn with_length(length: Length) -> Self {
121✔
141
                Self {
117✔
142
                    length,
143
                    $(
144
                        alignment: yes_to!(None, $alignment_yes),
117✔
145
                    )?
146
                    $(
147
                        year_style: yes_to!(None, $yearstyle_yes),
65✔
148
                    )?
149
                    $(
150
                        time_precision: yes_to!(None, $timeprecision_yes),
61✔
151
                    )?
152
                }
153
            }
121✔
154
            #[doc = concat!("Creates a ", stringify!($type), " skeleton with a long length.")]
155
            pub const fn long() -> Self {
40✔
156
                Self::with_length(Length::Long)
40✔
157
            }
40✔
158
            #[doc = concat!("Creates a ", stringify!($type), " skeleton with a medium length.")]
159
            pub const fn medium() -> Self {
45✔
160
                Self::with_length(Length::Medium)
45✔
161
            }
45✔
162
            #[doc = concat!("Creates a ", stringify!($type), " skeleton with a short length.")]
163
            pub const fn short() -> Self {
36✔
164
                Self::with_length(Length::Short)
36✔
165
            }
36✔
166
        }
167
    };
168
}
169

170
macro_rules! impl_marker_with_options {
171
    (
172
        $(#[$attr:meta])*
173
        $type:ident,
174
        $(sample_length: $sample_length:ident,)?
175
        $(date_fields: $date_fields:expr,)?
176
        $(alignment: $alignment_yes:ident,)?
177
        $(year_style: $yearstyle_yes:ident,)?
178
        $(time_precision: $timeprecision_yes:ident,)?
179
        $(length_override: $length_override:ident,)?
180
    ) => {
181
        $(#[$attr])*
182
        #[derive(Debug, Copy, Clone, PartialEq, Eq)]
8✔
183
        #[non_exhaustive]
184
        pub struct $type {
185
            $(
186
                /// The desired length of the formatted string.
187
                ///
188
                /// See: [`Length`]
189
                pub length: datetime_marker_helper!(@option/length, $sample_length),
4✔
190
            )?
191
            $(
192
                /// Whether fields should be aligned for a column-like layout.
193
                ///
194
                /// See: [`Alignment`]
195
                pub alignment: datetime_marker_helper!(@option/alignment, $alignment_yes),
4✔
196
            )?
197
            $(
198
                /// When to display the era field in the formatted string.
199
                ///
200
                /// See: [`YearStyle`]
201
                pub year_style: datetime_marker_helper!(@option/yearstyle, $yearstyle_yes),
1✔
202
            )?
203
            $(
204
                /// How precisely to display the time of day
205
                ///
206
                /// See: [`TimePrecision`]
207
                pub time_precision: datetime_marker_helper!(@option/timeprecision, $timeprecision_yes),
2✔
208
            )?
209
        }
210
        impl $type {
211
            pub(crate) fn to_raw_options(self) -> RawOptions {
370✔
212
                RawOptions {
370✔
213
                    length: yes_or!(Some(self.length), $(Some(Length::$length_override))?),
370✔
214
                    date_fields: yes_or!(None, $($date_fields)?),
61✔
215
                    alignment: ternary!(self.alignment, None, $($alignment_yes)?),
341✔
216
                    year_style: ternary!(self.year_style, None, $($yearstyle_yes)?),
370✔
217
                    time_precision: ternary!(self.time_precision, None, $($timeprecision_yes)?),
370✔
218
                }
219
            }
370✔
220
            /// Builds this field set, removing the needed options from the builder.
221
            pub(crate) fn take_from_builder(
1,004✔
222
                options: &mut builder::FieldSetBuilder
223
            ) -> Self {
224
                Self {
673✔
225
                    $(length: yes_to!(options.length.take().unwrap_or_default(), $sample_length),)?
1,004✔
226
                    $(alignment: yes_to!(options.alignment.take(), $alignment_yes),)?
949✔
227
                    $(year_style: yes_to!(options.year_style.take(), $yearstyle_yes),)?
297✔
228
                    $(time_precision: yes_to!(options.time_precision.take(), $timeprecision_yes),)?
442✔
229
                }
230
            }
1,004✔
231
        }
232
        $(
233
            impl $type {
234
                /// Sets the alignment option.
235
                pub const fn with_alignment(mut self, alignment: Alignment) -> Self {
7✔
236
                    self.alignment = Some(yes_to!(alignment, $alignment_yes));
7✔
237
                    self
7✔
238
                }
7✔
239
            }
240
        )?
241
        $(
242
            impl $type {
243
                /// Sets the year style option.
244
                pub const fn with_year_style(mut self, year_style: YearStyle) -> Self {
6✔
245
                    self.year_style = Some(yes_to!(year_style, $yearstyle_yes));
6✔
246
                    self
6✔
247
                }
6✔
248
            }
249
        )?
250
        $(
251
            impl $type {
252
                /// Sets the time precision option.
253
                pub const fn with_time_precision(mut self, time_precision: TimePrecision) -> Self {
11✔
254
                    self.time_precision = Some(yes_to!(time_precision, $timeprecision_yes));
11✔
255
                    self
11✔
256
                }
11✔
257
                /// Sets the time precision to [`TimePrecision::Minute`]
258
                pub fn hm(mut self) -> Self {
20✔
259
                    self.time_precision = Some(TimePrecision::Minute);
20✔
260
                    self
20✔
261
                }
20✔
262
            }
263
        )?
264
    };
265
}
266

267
macro_rules! impl_combo_get_field {
268
    ($type:ident, $composite:ident, $enum:ident, $variant:path) => {
269
        impl GetField<CompositeFieldSet> for Combo<$type, $variant> {
270
            #[inline]
271
            fn get_field(&self) -> CompositeFieldSet {
×
272
                CompositeFieldSet::$composite(Combo::new(self.dt().to_enum(), self.z().to_enum()))
×
273
            }
×
274
        }
275
        impl Combo<$type, $variant> {
276
            /// Convert this specific [`Combo`] into a more general [`Combo`].
277
            /// Useful when adding to the field of a [`CompositeFieldSet`].
278
            ///
279
            /// [`CompositeFieldSet`]: enums::CompositeFieldSet
280
            pub fn into_enums(self) -> Combo<$enum, ZoneFieldSet> {
1✔
281
                Combo::new(self.dt().to_enum(), self.z().to_enum())
1✔
282
            }
1✔
283
        }
284
    };
285
}
286

287
macro_rules! impl_zone_combo_helpers {
288
    (
289
        $type:ident,
290
        $composite:ident,
291
        $enum:ident
292
    ) => {
293
        impl $type {
294
            #[inline]
295
            /// Associates this field set with a time zone field set.
296
            pub fn zone<Z: ZoneMarkers>(self, zone: Z) -> Combo<Self, Z> {
×
297
                Combo::new(self, zone)
×
298
            }
×
299
        }
300
        impl_combo_get_field!($type, $composite, $enum, zone::SpecificLong);
301
        impl_combo_get_field!($type, $composite, $enum, zone::SpecificShort);
302
        impl_combo_get_field!($type, $composite, $enum, zone::LocalizedOffsetLong);
303
        impl_combo_get_field!($type, $composite, $enum, zone::LocalizedOffsetShort);
304
        impl_combo_get_field!($type, $composite, $enum, zone::GenericLong);
305
        impl_combo_get_field!($type, $composite, $enum, zone::GenericShort);
306
        impl_combo_get_field!($type, $composite, $enum, zone::Location);
307
    };
308
}
309

310
/// Internal helper macro used by [`impl_date_marker`] and [`impl_calendar_period_marker`]
311
macro_rules! impl_date_or_calendar_period_marker {
312
    (
313
        $(#[$attr:meta])*
314
        // The name of the type being created.
315
        $type:ident,
316
        // A plain language description of the field set for documentation.
317
        description = $description:literal,
318
        // Length of the sample string below.
319
        sample_length = $sample_length:ident,
320
        // A sample string. A docs test will be generated!
321
        sample = $sample:literal,
322
        // Whether years can occur.
323
        $(years = $years_yes:ident,)?
324
        // Whether months can occur.
325
        $(months = $months_yes:ident,)?
326
        // Whether weekdays can occur.
327
        $(weekdays = $weekdays_yes:ident,)?
328
        // Whether the input should contain years.
329
        $(input_year = $year_yes:ident,)?
330
        // Whether the input should contain months.
331
        $(input_month = $month_yes:ident,)?
332
        // Whether the input should contain the day of the month.
333
        $(input_day_of_month = $day_of_month_yes:ident,)?
334
        // Whether the input should contain the day of the week.
335
        $(input_day_of_week = $day_of_week_yes:ident,)?
336
        // Whether the input should contain the day of the year.
337
        $(input_day_of_year = $day_of_year_yes:ident,)?
338
        // Whether the input should declare its calendar kind.
339
        $(input_any_calendar_kind = $any_calendar_kind_yes:ident,)?
340
        // Whether the alignment option should be available.
341
        // According to UTS 35, it should be available with years, months, and days.
342
        $(option_alignment = $option_alignment_yes:ident,)?
343
    ) => {
344
        impl_marker_with_options!(
345
            #[doc = concat!("**“", $sample, "**” ⇒ ", $description)]
346
            ///
347
            /// This is a field set marker. For more information, see [`fieldsets`](crate::fieldsets).
348
            ///
349
            /// # Examples
350
            ///
351
            /// In [`DateTimeFormatter`](crate::neo::DateTimeFormatter):
352
            ///
353
            /// ```
354
            /// use icu::datetime::input::Date;
355
            /// use icu::datetime::DateTimeFormatter;
356
            #[doc = concat!("use icu::datetime::fieldsets::", stringify!($type), ";")]
357
            /// use icu::locale::locale;
358
            /// use writeable::assert_writeable_eq;
359
            #[doc = concat!("let fmt = DateTimeFormatter::<", stringify!($type), ">::try_new(")]
360
            ///     locale!("en").into(),
361
            #[doc = concat!("    ", length_option_helper!($type, $sample_length), ",")]
362
            /// )
363
            /// .unwrap();
364
            /// let dt = Date::try_new_iso(2024, 5, 17).unwrap();
365
            ///
366
            /// assert_writeable_eq!(
367
            ///     fmt.format(&dt),
368
            #[doc = concat!("    \"", $sample, "\"")]
369
            /// );
370
            /// ```
371
            ///
372
            /// In [`FixedCalendarDateTimeFormatter`](crate::neo::FixedCalendarDateTimeFormatter):
373
            ///
374
            /// ```
375
            /// use icu::datetime::input::Date;
376
            /// use icu::calendar::Gregorian;
377
            /// use icu::datetime::FixedCalendarDateTimeFormatter;
378
            #[doc = concat!("use icu::datetime::fieldsets::", stringify!($type), ";")]
379
            /// use icu::locale::locale;
380
            /// use writeable::assert_writeable_eq;
381
            ///
382
            #[doc = concat!("let fmt = FixedCalendarDateTimeFormatter::<Gregorian, ", stringify!($type), ">::try_new(")]
383
            ///     locale!("en").into(),
384
            #[doc = concat!("    ", length_option_helper!($type, $sample_length), ",")]
385
            /// )
386
            /// .unwrap();
387
            /// let dt = Date::try_new_gregorian(2024, 5, 17).unwrap();
388
            ///
389
            /// assert_writeable_eq!(
390
            ///     fmt.format(&dt),
391
            #[doc = concat!("    \"", $sample, "\"")]
392
            /// );
393
            /// ```
394
            $(#[$attr])*
395
            $type,
396
            sample_length: $sample_length,
397
            date_fields: Some(builder::DateFields::$type),
161✔
398
            $(alignment: $option_alignment_yes,)?
399
            $(year_style: $year_yes,)?
400
        );
401
        impl_marker_length_constructors!(
402
            $type,
403
            $(alignment: $option_alignment_yes,)?
404
            $(year_style: $year_yes,)?
405
        );
406
        impl UnstableSealed for $type {}
407
        impl DateTimeNamesMarker for $type {
408
            type YearNames = datetime_marker_helper!(@names/year, $($years_yes)?);
409
            type MonthNames = datetime_marker_helper!(@names/month, $($months_yes)?);
410
            type WeekdayNames = datetime_marker_helper!(@names/weekday, $($weekdays_yes)?);
411
            type DayPeriodNames = datetime_marker_helper!(@names/dayperiod,);
412
            type ZoneEssentials = datetime_marker_helper!(@names/zone/essentials,);
413
            type ZoneLocations = datetime_marker_helper!(@names/zone/locations,);
414
            type ZoneLocationsRoot = datetime_marker_helper!(@names/zone/locations_root,);
415
            type ZoneExemplars = datetime_marker_helper!(@names/zone/exemplar,);
416
            type ZoneExemplarsRoot = datetime_marker_helper!(@names/zone/exemplar_root,);
417
            type ZoneGenericLong = datetime_marker_helper!(@names/zone/generic_long,);
418
            type ZoneGenericShort = datetime_marker_helper!(@names/zone/generic_short,);
419
            type ZoneStandardLong = datetime_marker_helper!(@names/zone/standard_long,);
420
            type ZoneSpecificLong = datetime_marker_helper!(@names/zone/specific_long,);
421
            type ZoneSpecificShort = datetime_marker_helper!(@names/zone/specific_short,);
422
            type MetazoneLookup = datetime_marker_helper!(@names/zone/metazone_periods,);
423
        }
424
        impl DateInputMarkers for $type {
425
            type YearInput = datetime_marker_helper!(@input/year, $($year_yes)?);
426
            type MonthInput = datetime_marker_helper!(@input/month, $($month_yes)?);
427
            type DayOfMonthInput = datetime_marker_helper!(@input/day_of_month, $($day_of_month_yes)?);
428
            type DayOfYearInput = datetime_marker_helper!(@input/day_of_year, $($day_of_year_yes)?);
429
            type DayOfWeekInput = datetime_marker_helper!(@input/day_of_week, $($day_of_week_yes)?);
430
        }
431
        impl<C: CldrCalendar> TypedDateDataMarkers<C> for $type {
432
            type DateSkeletonPatternsV1 = datetime_marker_helper!(@dates/typed, yes);
433
            type YearNamesV1 = datetime_marker_helper!(@years/typed, $($years_yes)?);
434
            type MonthNamesV1 = datetime_marker_helper!(@months/typed, $($months_yes)?);
435
            type WeekdayNamesV1 = datetime_marker_helper!(@weekdays, $($weekdays_yes)?);
436
        }
437
        impl DateDataMarkers for $type {
438
            type Skel = datetime_marker_helper!(@calmarkers, yes);
439
            type Year = datetime_marker_helper!(@calmarkers, $($years_yes)?);
440
            type Month = datetime_marker_helper!(@calmarkers, $($months_yes)?);
441
            type WeekdayNamesV1 = datetime_marker_helper!(@weekdays, $($weekdays_yes)?);
442
        }
443
        impl DateTimeMarkers for $type {
444
            type D = Self;
445
            type T = ();
446
            type Z = ();
447
            type GluePatternV1 = datetime_marker_helper!(@glue,);
448
        }
449
    };
450
}
451

452
/// Implements a field set of date fields.
453
///
454
/// Several arguments to this macro are required, and the rest are optional.
455
/// The optional arguments should be written as `key = yes,` if that parameter
456
/// should be included.
457
///
458
/// See [`impl_date_marker`].
459
macro_rules! impl_date_marker {
460
    (
461
        $(#[$attr:meta])*
462
        $type:ident,
463
        $type_time:ident,
464
        description = $description:literal,
465
        sample_length = $sample_length:ident,
466
        sample = $sample:literal,
467
        sample_time = $sample_time:literal,
468
        $(years = $years_yes:ident,)?
469
        $(months = $months_yes:ident,)?
470
        $(dates = $dates_yes:ident,)?
471
        $(weekdays = $weekdays_yes:ident,)?
472
        $(input_year = $year_yes:ident,)?
473
        $(input_month = $month_yes:ident,)?
474
        $(input_day_of_month = $day_of_month_yes:ident,)?
475
        $(input_day_of_week = $day_of_week_yes:ident,)?
476
        $(input_day_of_year = $day_of_year_yes:ident,)?
477
        $(input_any_calendar_kind = $any_calendar_kind_yes:ident,)?
478
        $(option_alignment = $option_alignment_yes:ident,)?
479
    ) => {
480
        impl_date_or_calendar_period_marker!(
481
            $(#[$attr])*
482
            $type,
483
            description = $description,
484
            sample_length = $sample_length,
485
            sample = $sample,
486
            $(years = $years_yes,)?
487
            $(months = $months_yes,)?
488
            $(dates = $dates_yes,)?
489
            $(weekdays = $weekdays_yes,)?
490
            $(input_year = $year_yes,)?
491
            $(input_month = $month_yes,)?
492
            $(input_day_of_month = $day_of_month_yes,)?
493
            $(input_day_of_week = $day_of_week_yes,)?
494
            $(input_day_of_year = $day_of_year_yes,)?
495
            $(input_any_calendar_kind = $any_calendar_kind_yes,)?
496
            $(option_alignment = $option_alignment_yes,)?
497
        );
498
        impl_zone_combo_helpers!($type, DateZone, DateFieldSet);
499
        impl_composite!($type, Date, DateFieldSet);
500
        impl_marker_with_options!(
501
            #[doc = concat!("**“", $sample_time, "**” ⇒ ", $description, " with time")]
502
            ///
503
            /// # Examples
504
            ///
505
            /// In [`DateTimeFormatter`](crate::neo::DateTimeFormatter):
506
            ///
507
            /// ```
508
            /// use icu::datetime::input::Date;
509
            /// use icu::datetime::DateTimeFormatter;
510
            #[doc = concat!("use icu::datetime::fieldsets::", stringify!($type_time), ";")]
511
            /// use icu::locale::locale;
512
            /// use icu::datetime::input::{DateTime, Time};
513
            /// use writeable::assert_writeable_eq;
514
            ///
515
            #[doc = concat!("let fmt = DateTimeFormatter::try_new(")]
516
            ///     locale!("en").into(),
517
            #[doc = concat!("    ", length_option_helper!($type_time, $sample_length), ",")]
518
            /// )
519
            /// .unwrap();
520
            /// let dt = DateTime { date: Date::try_new_iso(2024, 5, 17).unwrap(), time: Time::try_new(15, 47, 50, 0).unwrap() };
521
            ///
522
            /// assert_writeable_eq!(
523
            ///     fmt.format(&dt),
524
            #[doc = concat!("    \"", $sample_time, "\"")]
525
            /// );
526
            /// ```
527
            ///
528
            /// In [`FixedCalendarDateTimeFormatter`](crate::neo::FixedCalendarDateTimeFormatter):
529
            ///
530
            /// ```
531
            /// use icu::datetime::input::Date;
532
            /// use icu::calendar::Gregorian;
533
            /// use icu::datetime::FixedCalendarDateTimeFormatter;
534
            #[doc = concat!("use icu::datetime::fieldsets::", stringify!($type_time), ";")]
535
            /// use icu::locale::locale;
536
            /// use icu::datetime::input::{DateTime, Time};
537
            /// use writeable::assert_writeable_eq;
538
            ///
539
            #[doc = concat!("let fmt = FixedCalendarDateTimeFormatter::try_new(")]
540
            ///     locale!("en").into(),
541
            #[doc = concat!("    ", length_option_helper!($type_time, $sample_length), ",")]
542
            /// )
543
            /// .unwrap();
544
            /// let dt = DateTime { date: Date::try_new_gregorian(2024, 5, 17).unwrap(), time: Time::try_new(15, 47, 50, 0).unwrap() };
545
            ///
546
            /// assert_writeable_eq!(
547
            ///     fmt.format(&dt),
548
            #[doc = concat!("    \"", $sample_time, "\"")]
549
            /// );
550
            /// ```
551
            $(#[$attr])*
552
            $type_time,
553
            sample_length: $sample_length,
554
            date_fields: Some(builder::DateFields::$type),
148✔
555
            alignment: yes,
556
            $(year_style: $year_yes,)?
557
            time_precision: yes,
558
        );
559
        impl_marker_length_constructors!(
560
            $type_time,
561
            alignment: yes,
562
            $(year_style: $year_yes,)?
563
            time_precision: yes,
564
        );
565
        impl_zone_combo_helpers!($type_time, DateTimeZone, DateAndTimeFieldSet);
566
        impl UnstableSealed for $type_time {}
567
        impl DateTimeNamesMarker for $type_time {
568
            type YearNames = datetime_marker_helper!(@names/year, $($years_yes)?);
569
            type MonthNames = datetime_marker_helper!(@names/month, $($months_yes)?);
570
            type WeekdayNames = datetime_marker_helper!(@names/weekday, $($weekdays_yes)?);
571
            type DayPeriodNames = datetime_marker_helper!(@names/dayperiod, yes);
572
            type ZoneEssentials = datetime_marker_helper!(@names/zone/essentials,);
573
            type ZoneLocations = datetime_marker_helper!(@names/zone/locations,);
574
            type ZoneLocationsRoot = datetime_marker_helper!(@names/zone/locations_root,);
575
            type ZoneExemplars = datetime_marker_helper!(@names/zone/exemplar,);
576
            type ZoneExemplarsRoot = datetime_marker_helper!(@names/zone/exemplar_root,);
577
            type ZoneGenericLong = datetime_marker_helper!(@names/zone/generic_long,);
578
            type ZoneGenericShort = datetime_marker_helper!(@names/zone/generic_short,);
579
            type ZoneStandardLong = datetime_marker_helper!(@names/zone/standard_long,);
580
            type ZoneSpecificLong = datetime_marker_helper!(@names/zone/specific_long,);
581
            type ZoneSpecificShort = datetime_marker_helper!(@names/zone/specific_short,);
582
            type MetazoneLookup = datetime_marker_helper!(@names/zone/metazone_periods,);
583
        }
584
        impl DateInputMarkers for $type_time {
585
            type YearInput = datetime_marker_helper!(@input/year, $($year_yes)?);
586
            type MonthInput = datetime_marker_helper!(@input/month, $($month_yes)?);
587
            type DayOfMonthInput = datetime_marker_helper!(@input/day_of_month, $($day_of_month_yes)?);
588
            type DayOfYearInput = datetime_marker_helper!(@input/day_of_year, $($day_of_year_yes)?);
589
            type DayOfWeekInput = datetime_marker_helper!(@input/day_of_week, $($day_of_week_yes)?);
590
        }
591
        impl<C: CldrCalendar> TypedDateDataMarkers<C> for $type_time {
592
            type DateSkeletonPatternsV1 = datetime_marker_helper!(@dates/typed, yes);
593
            type YearNamesV1 = datetime_marker_helper!(@years/typed, $($years_yes)?);
594
            type MonthNamesV1 = datetime_marker_helper!(@months/typed, $($months_yes)?);
595
            type WeekdayNamesV1 = datetime_marker_helper!(@weekdays, $($weekdays_yes)?);
596
        }
597
        impl DateDataMarkers for $type_time {
598
            type Skel = datetime_marker_helper!(@calmarkers, yes);
599
            type Year = datetime_marker_helper!(@calmarkers, $($years_yes)?);
600
            type Month = datetime_marker_helper!(@calmarkers, $($months_yes)?);
601
            type WeekdayNamesV1 = datetime_marker_helper!(@weekdays, $($weekdays_yes)?);
602
        }
603
        impl TimeMarkers for $type_time {
604
            // TODO: Consider making dayperiods optional again
605
            type DayPeriodNamesV1 = datetime_marker_helper!(@dayperiods, yes);
606
            type TimeSkeletonPatternsV1 = datetime_marker_helper!(@times, yes);
607
            type HourInput = datetime_marker_helper!(@input/hour, yes);
608
            type MinuteInput = datetime_marker_helper!(@input/minute, yes);
609
            type SecondInput = datetime_marker_helper!(@input/second, yes);
610
            type NanosecondInput = datetime_marker_helper!(@input/Nanosecond, yes);
611
        }
612
        impl DateTimeMarkers for $type_time {
613
            type D = Self;
614
            type T = Self;
615
            type Z = ();
616
            type GluePatternV1 = datetime_marker_helper!(@glue, yes);
617
        }
618
        impl_composite!($type_time, DateTime, DateAndTimeFieldSet);
619
        impl $type_time {
620
            pub(crate) fn to_date_field_set(self) -> $type {
140✔
621
                $type {
122✔
622
                    length: self.length,
140✔
623
                    $(alignment: yes_to!(self.alignment, $option_alignment_yes),)?
132✔
624
                    $(year_style: yes_to!(self.year_style, $years_yes),)?
122✔
625
                }
626
            }
140✔
627
        }
628
    };
629
}
630

631
/// Implements a field set of calendar period fields.
632
///
633
/// Several arguments to this macro are required, and the rest are optional.
634
/// The optional arguments should be written as `key = yes,` if that parameter
635
/// should be included.
636
///
637
/// See [`impl_date_marker`].
638
macro_rules! impl_calendar_period_marker {
639
    (
640
        $(#[$attr:meta])*
641
        $type:ident,
642
        description = $description:literal,
643
        sample_length = $sample_length:ident,
644
        sample = $sample:literal,
645
        $(years = $years_yes:ident,)?
646
        $(months = $months_yes:ident,)?
647
        $(dates = $dates_yes:ident,)?
648
        $(input_year = $year_yes:ident,)?
649
        $(input_month = $month_yes:ident,)?
650
        $(input_any_calendar_kind = $any_calendar_kind_yes:ident,)?
651
        $(option_alignment = $option_alignment_yes:ident,)?
652
    ) => {
653
        impl_date_or_calendar_period_marker!(
654
            $(#[$attr])*
655
            $type,
656
            description = $description,
657
            sample_length = $sample_length,
658
            sample = $sample,
659
            $(years = $years_yes,)?
660
            $(months = $months_yes,)?
661
            $(dates = $dates_yes,)?
662
            $(input_year = $year_yes,)?
663
            $(input_month = $month_yes,)?
664
            $(input_any_calendar_kind = $any_calendar_kind_yes,)?
665
            $(option_alignment = $option_alignment_yes,)?
666
        );
667
        impl_composite!($type, CalendarPeriod, CalendarPeriodFieldSet);
668
    };
669
}
670

671
/// Implements a field set of time fields.
672
///
673
/// Several arguments to this macro are required, and the rest are optional.
674
/// The optional arguments should be written as `key = yes,` if that parameter
675
/// should be included.
676
///
677
/// Documentation for each option is shown inline below.
678
macro_rules! impl_time_marker {
679
    (
680
        $(#[$attr:meta])*
681
        // The name of the type being created.
682
        $type:ident,
683
        // A plain language description of the field set for documentation.
684
        description = $description:literal,
685
        // Length of the sample string below.
686
        sample_length = $sample_length:ident,
687
        // A sample string. A docs test will be generated!
688
        sample = $sample:literal,
689
        // Whether day periods can occur.
690
        $(dayperiods = $dayperiods_yes:ident,)?
691
        // Whether the input should include hours.
692
        $(input_hour = $hour_yes:ident,)?
693
        // Whether the input should contain minutes.
694
        $(input_minute = $minute_yes:ident,)?
695
        // Whether the input should contain seconds.
696
        $(input_second = $second_yes:ident,)?
697
        // Whether the input should contain fractional seconds.
698
        $(input_subsecond = $Nanosecond_yes:ident,)?
699
    ) => {
700
        impl_marker_with_options!(
701
            #[doc = concat!("**“", $sample, "**” ⇒ ", $description)]
702
            ///
703
            /// # Examples
704
            ///
705
            /// ```
706
            /// use icu::datetime::input::Time;
707
            /// use icu::datetime::NoCalendarFormatter;
708
            #[doc = concat!("use icu::datetime::fieldsets::", stringify!($type), ";")]
709
            /// use icu::locale::locale;
710
            /// use writeable::assert_writeable_eq;
711
            ///
712
            #[doc = concat!("let fmt = NoCalendarFormatter::try_new(")]
713
            ///     locale!("en").into(),
714
            #[doc = concat!("    ", length_option_helper!($type, $sample_length), ",")]
715
            /// )
716
            /// .unwrap();
717
            /// let time = Time::try_new(15, 47, 50, 0).unwrap();
718
            ///
719
            /// assert_writeable_eq!(
720
            ///     fmt.format(&time),
721
            #[doc = concat!("    \"", $sample, "\"")]
722
            /// );
723
            /// ```
724
            $(#[$attr])*
725
            $type,
726
            sample_length: $sample_length,
727
            alignment: yes,
728
            time_precision: yes,
729
        );
730
        impl_marker_length_constructors!(
731
            $type,
732
            alignment: yes,
733
            time_precision: yes,
734
        );
735
        impl_zone_combo_helpers!($type, TimeZone, TimeFieldSet);
736
        impl UnstableSealed for $type {}
737
        impl DateTimeNamesMarker for $type {
738
            type YearNames = datetime_marker_helper!(@names/year,);
739
            type MonthNames = datetime_marker_helper!(@names/month,);
740
            type WeekdayNames = datetime_marker_helper!(@names/weekday,);
741
            type DayPeriodNames = datetime_marker_helper!(@names/dayperiod, $($dayperiods_yes)?);
742
            type ZoneEssentials = datetime_marker_helper!(@names/zone/essentials,);
743
            type ZoneLocations = datetime_marker_helper!(@names/zone/locations,);
744
            type ZoneLocationsRoot = datetime_marker_helper!(@names/zone/locations_root,);
745
            type ZoneExemplars = datetime_marker_helper!(@names/zone/exemplar,);
746
            type ZoneExemplarsRoot = datetime_marker_helper!(@names/zone/exemplar_root,);
747
            type ZoneGenericLong = datetime_marker_helper!(@names/zone/generic_long,);
748
            type ZoneGenericShort = datetime_marker_helper!(@names/zone/generic_short,);
749
            type ZoneStandardLong = datetime_marker_helper!(@names/zone/standard_long,);
750
            type ZoneSpecificLong = datetime_marker_helper!(@names/zone/specific_long,);
751
            type ZoneSpecificShort = datetime_marker_helper!(@names/zone/specific_short,);
752
            type MetazoneLookup = datetime_marker_helper!(@names/zone/metazone_periods,);
753
        }
754
        impl TimeMarkers for $type {
755
            type DayPeriodNamesV1 = datetime_marker_helper!(@dayperiods, $($dayperiods_yes)?);
756
            type TimeSkeletonPatternsV1 = datetime_marker_helper!(@times, yes);
757
            type HourInput = datetime_marker_helper!(@input/hour, $($hour_yes)?);
758
            type MinuteInput = datetime_marker_helper!(@input/minute, $($minute_yes)?);
759
            type SecondInput = datetime_marker_helper!(@input/second, $($second_yes)?);
760
            type NanosecondInput = datetime_marker_helper!(@input/Nanosecond, $($Nanosecond_yes)?);
761
        }
762
        impl DateTimeMarkers for $type {
763
            type D = ();
764
            type T = Self;
765
            type Z = ();
766
            type GluePatternV1 = datetime_marker_helper!(@glue,);
767
        }
768
        impl_composite!($type, Time, TimeFieldSet);
769
    };
770
}
771

772
/// Implements a field set of time zone fields.
773
///
774
/// Several arguments to this macro are required, and the rest are optional.
775
/// The optional arguments should be written as `key = yes,` if that parameter
776
/// should be included.
777
///
778
/// Documentation for each option is shown inline below.
779
macro_rules! impl_zone_marker {
780
    (
781
        $(#[$attr:meta])*
782
        // The name of the type being created.
783
        $type:ident,
784
        // A plain language description of the field set for documentation.
785
        description = $description:literal,
786
        // Length of the skeleton if this is the only field.
787
        length_override = $length_override:ident,
788
        // A sample string. A docs test will be generated!
789
        sample = $sample:literal,
790
        // The field symbol and field length.
791
        field = $field:expr,
792
        // The type in ZoneFieldSet for this field set
793
        // Whether zone-essentials should be loaded.
794
        $(zone_essentials = $zone_essentials_yes:ident,)?
795
        // Whether locations names are needed.
796
        $(zone_locations = $zone_locations_yes:ident,)?
797
        // Whether exemplar city names are needed.
798
        $(zone_exemplars = $zone_exemplars_yes:ident,)?
799
        // Whether generic long names are needed.
800
        $(zone_generic_long = $zone_generic_long_yes:ident,)?
801
        // Whether generic short names are needed.
802
        $(zone_generic_short = $zone_generic_short_yes:ident,)?
803
        // Whether standard long names are needed.
804
        $(zone_standard_long = $zone_standard_long_yes:ident,)?
805
        // Whether specific long names are needed.
806
        $(zone_specific_long = $zone_specific_long_yes:ident,)?
807
        // Whether specific short names are needed.
808
        $(zone_specific_short = $zone_specific_short_yes:ident,)?
809
        // Whether metazone periods are needed
810
        $(metazone_periods = $metazone_periods_yes:ident,)?
811
        // Whether to require the TimeZone
812
        $(input_tzid = $tzid_input_yes:ident,)?
813
        // Whether to require the TimeZoneVariant
814
        $(input_variant = $variant_input_yes:ident,)?
815
        // Whether to require the Local Time
816
        $(input_localtime = $localtime_input_yes:ident,)?
817
    ) => {
818
        #[doc = concat!("**“", $sample, "**” ⇒ ", $description)]
819
        ///
820
        /// # Examples
821
        ///
822
        /// ```
823
        /// use icu::datetime::input::Date;
824
        /// use icu::datetime::input::{Time, TimeZone,TimeZoneInfo,  UtcOffset};
825
        /// use icu::datetime::NoCalendarFormatter;
826
        /// use icu::time::zone::TimeZoneVariant;
827
        #[doc = concat!("use icu::datetime::fieldsets::zone::", stringify!($type), ";")]
828
        /// use icu::locale::locale;
829
        /// use tinystr::tinystr;
830
        /// use writeable::assert_writeable_eq;
831
        ///
832
        /// let fmt = NoCalendarFormatter::try_new(
833
        ///     locale!("en").into(),
834
        #[doc = concat!("    ", stringify!($type))]
835
        /// )
836
        /// .unwrap();
837
        ///
838
        /// // Time zone info for America/Chicago in the summer
839
        /// let zone = TimeZone(tinystr!(8, "uschi"))
840
        ///     .with_offset("-05".parse().ok())
841
        ///     .at_time((Date::try_new_iso(2022, 8, 29).unwrap(), Time::midnight()))
842
        ///     .with_zone_variant(TimeZoneVariant::Daylight);
843
        ///
844
        /// assert_writeable_eq!(
845
        ///     fmt.format(&zone),
846
        #[doc = concat!("    \"", $sample, "\"")]
847
        /// );
848
        /// ```
849
        $(#[$attr])*
850
        #[derive(Debug, Copy, Clone, PartialEq, Eq)]
1✔
851
        #[allow(clippy::exhaustive_structs)] // singleton marker
852
        pub struct $type;
853
        impl UnstableSealed for $type {}
854
        impl DateTimeNamesMarker for $type {
855
            type YearNames = datetime_marker_helper!(@names/year,);
856
            type MonthNames = datetime_marker_helper!(@names/month,);
857
            type WeekdayNames = datetime_marker_helper!(@names/weekday,);
858
            type DayPeriodNames = datetime_marker_helper!(@names/dayperiod,);
859
            type ZoneEssentials = datetime_marker_helper!(@names/zone/essentials, $($zone_essentials_yes)?);
860
            type ZoneLocations = datetime_marker_helper!(@names/zone/locations, $($zone_locations_yes)?);
861
            type ZoneLocationsRoot = datetime_marker_helper!(@names/zone/locations_root, $($zone_locations_yes)?);
862
            type ZoneExemplars = datetime_marker_helper!(@names/zone/exemplars, $($zone_exemplars_yes)?);
863
            type ZoneExemplarsRoot = datetime_marker_helper!(@names/zone/exemplars_root, $($zone_exemplars_yes)?);
864
            type ZoneGenericLong = datetime_marker_helper!(@names/zone/generic_long, $($zone_generic_long_yes)?);
865
            type ZoneGenericShort = datetime_marker_helper!(@names/zone/generic_short, $($zone_generic_short_yes)?);
866
            type ZoneStandardLong = datetime_marker_helper!(@names/zone/standard_long, $($zone_standard_long_yes)?);
867
            type ZoneSpecificLong = datetime_marker_helper!(@names/zone/specific_long, $($zone_specific_long_yes)?);
868
            type ZoneSpecificShort = datetime_marker_helper!(@names/zone/specific_short, $($zone_specific_short_yes)?);
869
            type MetazoneLookup = datetime_marker_helper!(@names/zone/metazone_periods, $($metazone_periods_yes)?);
870
        }
871
        impl ZoneMarkers for $type {
872
            type TimeZoneIdInput = datetime_marker_helper!(@input/timezone/id, $($tzid_input_yes)?);
873
            type TimeZoneOffsetInput = datetime_marker_helper!(@input/timezone/offset, yes);
874
            type TimeZoneVariantInput = datetime_marker_helper!(@input/timezone/variant, $($variant_input_yes)?);
875
            type TimeZoneLocalTimeInput = datetime_marker_helper!(@input/timezone/local_time, $($localtime_input_yes)?);
876
            type EssentialsV1 = datetime_marker_helper!(@data/zone/essentials, $($zone_essentials_yes)?);
877
            type LocationsV1 = datetime_marker_helper!(@data/zone/locations, $($zone_locations_yes)?);
878
            type LocationsRootV1 = datetime_marker_helper!(@data/zone/locations_root, $($zone_locations_yes)?);
879
            type ExemplarCitiesV1 = datetime_marker_helper!(@data/zone/exemplars, $($zone_exemplars_yes)?);
880
            type ExemplarCitiesRootV1 = datetime_marker_helper!(@data/zone/exemplars_root, $($zone_exemplars_yes)?);
881
            type GenericLongV1 = datetime_marker_helper!(@data/zone/generic_long, $($zone_generic_long_yes)?);
882
            type GenericShortV1 = datetime_marker_helper!(@data/zone/generic_short, $($zone_generic_short_yes)?);
883
            type StandardLongV1 = datetime_marker_helper!(@data/zone/standard_long, $($zone_standard_long_yes)?);
884
            type SpecificLongV1 = datetime_marker_helper!(@data/zone/specific_long, $($zone_specific_long_yes)?);
885
            type SpecificShortV1 = datetime_marker_helper!(@data/zone/specific_short, $($zone_specific_short_yes)?);
886
            type MetazonePeriodV1 = datetime_marker_helper!(@data/zone/metazone_periods, $($metazone_periods_yes)?);
887
        }
888
        impl DateTimeMarkers for $type {
889
            type D = ();
890
            type T = ();
891
            type Z = Self;
892
            type GluePatternV1 = datetime_marker_helper!(@glue,);
893
        }
894
        impl_composite!($type, Zone, ZoneFieldSet);
895
        impl $type {
896
            pub(crate) fn to_field(self) -> (fields::TimeZone, fields::FieldLength) {
131✔
897
                $field
898
            }
131✔
899
        }
900
    };
901
}
902

903
impl_date_marker!(
904
    /// This format may use ordinal formatting, such as "the 17th",
905
    /// in the future. See CLDR-18040.
906
    D,
907
    DT,
908
    description = "day of month (standalone)",
909
    sample_length = short,
910
    sample = "17",
911
    sample_time = "17, 3:47:50 PM",
912
    input_day_of_month = yes,
913
    input_any_calendar_kind = yes,
914
    option_alignment = yes,
915
);
916

917
impl_date_marker!(
918
    E,
919
    ET,
920
    description = "weekday (standalone)",
921
    sample_length = long,
922
    sample = "Friday",
923
    sample_time = "Friday 3:47:50 PM",
924
    weekdays = yes,
925
    input_day_of_week = yes,
926
);
927

928
impl_date_marker!(
929
    /// This format may use ordinal formatting, such as "Friday the 17th",
930
    /// in the future. See CLDR-18040.
931
    DE,
932
    DET,
933
    description = "day of month and weekday",
934
    sample_length = long,
935
    sample = "17 Friday",
936
    sample_time = "17 Friday, 3:47:50 PM",
937
    weekdays = yes,
938
    input_day_of_month = yes,
939
    input_day_of_week = yes,
940
    option_alignment = yes,
941
);
942

943
impl_date_marker!(
944
    MD,
945
    MDT,
946
    description = "month and day",
947
    sample_length = medium,
948
    sample = "May 17",
949
    sample_time = "May 17, 3:47:50 PM",
950
    months = yes,
951
    input_month = yes,
952
    input_day_of_month = yes,
953
    input_any_calendar_kind = yes,
954
    option_alignment = yes,
955
);
956

957
impl_date_marker!(
958
    /// See CLDR-18040 for progress on improving this format.
959
    MDE,
960
    MDET,
961
    description = "month, day, and weekday",
962
    sample_length = medium,
963
    sample = "Fri, May 17",
964
    sample_time = "Fri, May 17, 3:47:50 PM",
965
    months = yes,
966
    weekdays = yes,
967
    input_month = yes,
968
    input_day_of_month = yes,
969
    input_day_of_week = yes,
970
    input_any_calendar_kind = yes,
971
    option_alignment = yes,
972
);
973

974
impl_date_marker!(
975
    YMD,
976
    YMDT,
977
    description = "year, month, and day",
978
    sample_length = short,
979
    sample = "5/17/24",
980
    sample_time = "5/17/24, 3:47:50 PM",
981
    years = yes,
982
    months = yes,
983
    input_year = yes,
984
    input_month = yes,
985
    input_day_of_month = yes,
986
    input_any_calendar_kind = yes,
987
    option_alignment = yes,
988
);
989

990
impl_date_marker!(
991
    YMDE,
992
    YMDET,
993
    description = "year, month, day, and weekday",
994
    sample_length = short,
995
    sample = "Fri, 5/17/24",
996
    sample_time = "Fri, 5/17/24, 3:47:50 PM",
997
    years = yes,
998
    months = yes,
999
    weekdays = yes,
1000
    input_year = yes,
1001
    input_month = yes,
1002
    input_day_of_month = yes,
1003
    input_day_of_week = yes,
1004
    input_any_calendar_kind = yes,
1005
    option_alignment = yes,
1006
);
1007

1008
impl_calendar_period_marker!(
1009
    Y,
1010
    description = "year (standalone)",
1011
    sample_length = medium,
1012
    sample = "2024",
1013
    years = yes,
1014
    input_year = yes,
1015
    input_any_calendar_kind = yes,
1016
    option_alignment = yes,
1017
);
1018

1019
impl_calendar_period_marker!(
1020
    M,
1021
    description = "month (standalone)",
1022
    sample_length = long,
1023
    sample = "May",
1024
    months = yes,
1025
    input_month = yes,
1026
    input_any_calendar_kind = yes,
1027
    option_alignment = yes,
1028
);
1029

1030
impl_calendar_period_marker!(
1031
    YM,
1032
    description = "year and month",
1033
    sample_length = medium,
1034
    sample = "May 2024",
1035
    years = yes,
1036
    months = yes,
1037
    input_year = yes,
1038
    input_month = yes,
1039
    input_any_calendar_kind = yes,
1040
    option_alignment = yes,
1041
);
1042

1043
impl_time_marker!(
1044
    /// Hours can be switched between 12-hour and 24-hour time via the `u-hc` locale keyword
1045
    /// or [`DateTimeFormatterPreferences`].
1046
    ///
1047
    /// ```
1048
    /// use icu::datetime::input::Time;
1049
    /// use icu::datetime::fieldsets::T;
1050
    /// use icu::datetime::NoCalendarFormatter;
1051
    /// use icu::locale::locale;
1052
    /// use writeable::assert_writeable_eq;
1053
    ///
1054
    /// // By default, en-US uses 12-hour time and fr-FR uses 24-hour time,
1055
    /// // but we can set overrides.
1056
    ///
1057
    /// let formatter = NoCalendarFormatter::try_new(
1058
    ///     locale!("en-US-u-hc-h12").into(),
1059
    ///     T::short().hm(),
1060
    /// )
1061
    /// .unwrap();
1062
    /// assert_writeable_eq!(
1063
    ///     formatter.format(&Time::try_new(16, 12, 20, 0).unwrap()),
1064
    ///     "4:12 PM"
1065
    /// );
1066
    ///
1067
    /// let formatter = NoCalendarFormatter::try_new(
1068
    ///     locale!("en-US-u-hc-h23").into(),
1069
    ///     T::short().hm(),
1070
    /// )
1071
    /// .unwrap();
1072
    /// assert_writeable_eq!(
1073
    ///     formatter.format(&Time::try_new(16, 12, 20, 0).unwrap()),
1074
    ///     "16:12"
1075
    /// );
1076
    ///
1077
    /// let formatter = NoCalendarFormatter::try_new(
1078
    ///     locale!("fr-FR-u-hc-h12").into(),
1079
    ///     T::short().hm(),
1080
    /// )
1081
    /// .unwrap();
1082
    /// assert_writeable_eq!(
1083
    ///     formatter.format(&Time::try_new(16, 12, 20, 0).unwrap()),
1084
    ///     "4:12 PM"
1085
    /// );
1086
    ///
1087
    /// let formatter = NoCalendarFormatter::try_new(
1088
    ///     locale!("fr-FR-u-hc-h23").into(),
1089
    ///     T::short().hm(),
1090
    /// )
1091
    /// .unwrap();
1092
    /// assert_writeable_eq!(
1093
    ///     formatter.format(&Time::try_new(16, 12, 20, 0).unwrap()),
1094
    ///     "16:12"
1095
    /// );
1096
    /// ```
1097
    ///
1098
    /// Hour cycles `h11` and `h24` are supported, too:
1099
    ///
1100
    /// ```
1101
    /// use icu::datetime::input::Time;
1102
    /// use icu::datetime::fieldsets::T;
1103
    /// use icu::datetime::NoCalendarFormatter;
1104
    /// use icu::locale::locale;
1105
    /// use writeable::assert_writeable_eq;
1106
    ///
1107
    /// let formatter = NoCalendarFormatter::try_new(
1108
    ///     locale!("und-u-hc-h11").into(),
1109
    ///     T::short().hm(),
1110
    /// )
1111
    /// .unwrap();
1112
    ///
1113
    /// assert_writeable_eq!(
1114
    ///     formatter.format(&Time::try_new(0, 0, 0, 0).unwrap()),
1115
    ///     "0:00 AM"
1116
    /// );
1117
    ///
1118
    /// let formatter = NoCalendarFormatter::try_new(
1119
    ///     locale!("und-u-hc-h24").into(),
1120
    ///     T::short().hm(),
1121
    /// )
1122
    /// .unwrap();
1123
    ///
1124
    /// assert_writeable_eq!(
1125
    ///     formatter.format(&Time::try_new(0, 0, 0, 0).unwrap()),
1126
    ///     "24:00"
1127
    /// );
1128
    /// ```
1129
    ///
1130
    /// [`DateTimeFormatterPreferences`]: crate::DateTimeFormatterPreferences
1131
    T,
1132
    description = "time (locale-dependent hour cycle)",
1133
    sample_length = medium,
1134
    sample = "3:47:50 PM",
1135
    dayperiods = yes,
1136
    input_hour = yes,
1137
    input_minute = yes,
1138
    input_second = yes,
1139
    input_subsecond = yes,
1140
);
1141

1142
/// Time zone field sets
1143
pub mod zone {
1144
    use super::*;
1145
    impl_zone_marker!(
1146
        /// When a display name is unavailable, falls back to the localized offset format for short lengths, and
1147
        /// to the location format for long lengths:
1148
        ///
1149
        /// ```
1150
        /// use icu::datetime::input::Date;
1151
        /// use icu::datetime::input::{Time, TimeZone, TimeZoneInfo, UtcOffset};
1152
        /// use icu::calendar::Gregorian;
1153
        /// use icu::datetime::FixedCalendarDateTimeFormatter;
1154
        /// use icu::datetime::fieldsets::zone::{SpecificLong, SpecificShort};
1155
        /// use icu::locale::locale;
1156
        /// use icu::time::zone::TimeZoneVariant;
1157
        /// use tinystr::tinystr;
1158
        /// use writeable::assert_writeable_eq;
1159
        ///
1160
        /// // Time zone info for Europe/Istanbul in the winter
1161
        /// let zone = TimeZone(tinystr!(8, "trist"))
1162
        ///     .with_offset("+02".parse().ok())
1163
        ///     .at_time((Date::try_new_iso(2022, 1, 29).unwrap(), Time::midnight()))
1164
        ///     .with_zone_variant(TimeZoneVariant::Standard);
1165
        ///
1166
        /// let fmt = FixedCalendarDateTimeFormatter::<Gregorian, _>::try_new(
1167
        ///     locale!("en").into(),
1168
        ///     SpecificShort,
1169
        /// )
1170
        /// .unwrap();
1171
        ///
1172
        /// assert_writeable_eq!(
1173
        ///     fmt.format(&zone),
1174
        ///     "GMT+2"
1175
        /// );
1176
        ///
1177
        /// let fmt = FixedCalendarDateTimeFormatter::<Gregorian, _>::try_new(
1178
        ///     locale!("en").into(),
1179
        ///     SpecificLong,
1180
        /// )
1181
        /// .unwrap();
1182
        ///
1183
        /// assert_writeable_eq!(
1184
        ///     fmt.format(&zone),
1185
        ///     "Türkiye Standard Time"
1186
        /// );
1187
        /// ```
1188
        ///
1189
        /// This style requires a [`TimeZoneVariant`], so
1190
        /// only a full time zone info can be formatted with this style.
1191
        /// For example, [`TimeZoneInfo<AtTime>`] cannot be formatted.
1192
        ///
1193
        /// ```compile_fail,E0271
1194
        /// use icu::datetime::input::{Date, Iso};
1195
        /// use icu::datetime::FixedCalendarDateTimeFormatter;
1196
        /// use icu::datetime::fieldsets::zone::SpecificLong;
1197
        /// use icu::locale::locale;
1198
        /// use icu::datetime::input::{DateTime, Time, TimeZone, UtcOffset};
1199
        /// use icu::time::zone::TimeZoneVariant;
1200
        /// use tinystr::tinystr;
1201
        /// use writeable::assert_writeable_eq;
1202
        ///
1203
        /// let datetime = DateTime { date: Date::try_new_gregorian(2024, 10, 18).unwrap(), time: Time::midnight() };
1204
        /// let time_zone_basic = TimeZone(tinystr!(8, "uschi")).with_offset("-06".parse().ok());
1205
        /// let time_zone_at_time = time_zone_basic.at_time((datetime.date.to_iso(), datetime.time));
1206
        ///
1207
        /// let formatter = FixedCalendarDateTimeFormatter::try_new(
1208
        ///     locale!("en-US").into(),
1209
        ///     SpecificLong,
1210
        /// )
1211
        /// .unwrap();
1212
        ///
1213
        /// // error[E0271]: type mismatch resolving `<AtTime as TimeZoneModel>::TimeZoneVariant == TimeZoneVariant`
1214
        /// formatter.format(&time_zone_at_time);
1215
        /// ```
1216
        SpecificLong,
1217
        description = "time zone in specific non-location format, long length",
1218
        length_override = Long,
1219
        sample = "Central Daylight Time",
1220
        field = (fields::TimeZone::SpecificNonLocation, fields::FieldLength::Four),
16✔
1221
        zone_essentials = yes,
1222
        zone_locations = yes,
1223
        zone_standard_long = yes,
1224
        zone_specific_long = yes,
1225
        metazone_periods = yes,
1226
        input_tzid = yes,
1227
        input_variant = yes,
1228
        input_localtime = yes,
1229
    );
1230

1231
    impl_zone_marker!(
1232
        /// This style requires a [`TimeZoneVariant`], so
1233
        /// only a full time zone info can be formatted with this style.
1234
        /// For example, [`TimeZoneInfo<AtTime>`] cannot be formatted.
1235
        ///
1236
        /// ```compile_fail,E0271
1237
        /// use icu::datetime::input::{Date, Iso};
1238
        /// use icu::datetime::FixedCalendarDateTimeFormatter;
1239
        /// use icu::datetime::fieldsets::{T, zone::SpecificShort};
1240
        /// use icu::locale::locale;
1241
        /// use icu::datetime::input::{DateTime, Time, TimeZone, UtcOffset};
1242
        /// use icu::time::zone::TimeZoneVariant;
1243
        /// use tinystr::tinystr;
1244
        /// use writeable::assert_writeable_eq;
1245
        ///
1246
        /// let datetime = DateTime { Date::try_new_gregorian(2024, 10, 18).unwrap(), time: Time::midnight() };
1247
        /// let time_zone_basic = TimeZone(tinystr!(8, "uschi")).with_offset("-06".parse().ok());
1248
        /// let time_zone_at_time = time_zone_basic.at_time((datetime.date.to_iso(), datetime.time));
1249
        ///
1250
        /// let formatter = FixedCalendarDateTimeFormatter::try_new(
1251
        ///     locale!("en-US").into(),
1252
        ///     T::medium().zone(SpecificShort),
1253
        /// )
1254
        /// .unwrap();
1255
        ///
1256
        /// // error[E0271]: type mismatch resolving `<AtTime as TimeZoneModel>::TimeZoneVariant == TimeZoneVariant`
1257
        /// // note: required by a bound in `FixedCalendarDateTimeFormatter::<C, FSet>::format`
1258
        /// formatter.format(&time_zone_at_time);
1259
        /// ```
1260
        SpecificShort,
1261
        description = "time zone in specific non-location format, short length",
1262
        length_override = Short,
1263
        sample = "CDT",
1264
        field = (fields::TimeZone::SpecificNonLocation, fields::FieldLength::One),
16✔
1265
        zone_essentials = yes,
1266
        zone_specific_short = yes,
1267
        metazone_periods = yes,
1268
        input_tzid = yes,
1269
        input_variant = yes,
1270
        input_localtime = yes,
1271
    );
1272

1273
    impl_zone_marker!(
1274
        /// All shapes of time zones can be formatted with this style.
1275
        ///
1276
        /// ```
1277
        /// use icu::datetime::input::Date;
1278
        /// use icu::datetime::NoCalendarFormatter;
1279
        /// use icu::datetime::fieldsets::zone::LocalizedOffsetLong;
1280
        /// use icu::datetime::input::{Time, TimeZone, UtcOffset};
1281
        /// use icu::time::zone::TimeZoneVariant;
1282
        /// use tinystr::tinystr;
1283
        /// use icu::locale::locale;
1284
        /// use writeable::assert_writeable_eq;
1285
        ///
1286
        /// let utc_offset = "-06".parse().unwrap();
1287
        /// let time_zone_basic = TimeZone(tinystr!(8, "uschi")).with_offset(Some(utc_offset));
1288
        ///
1289
        /// let date = Date::try_new_iso(2024, 10, 18).unwrap();
1290
        /// let time = Time::midnight();
1291
        /// let time_zone_at_time = time_zone_basic.at_time((date, time));
1292
        ///
1293
        /// let time_zone_full = time_zone_at_time.with_zone_variant(TimeZoneVariant::Standard);
1294
        ///
1295
        /// let formatter = NoCalendarFormatter::try_new(
1296
        ///     locale!("en-US").into(),
1297
        ///     LocalizedOffsetLong,
1298
        /// )
1299
        /// .unwrap();
1300
        ///
1301
        /// assert_writeable_eq!(
1302
        ///     formatter.format(&utc_offset),
1303
        ///     "GMT-06:00"
1304
        /// );
1305
        ///
1306
        /// assert_writeable_eq!(
1307
        ///     formatter.format(&time_zone_basic),
1308
        ///     "GMT-06:00"
1309
        /// );
1310
        ///
1311
        /// assert_writeable_eq!(
1312
        ///     formatter.format(&time_zone_at_time),
1313
        ///     "GMT-06:00"
1314
        /// );
1315
        ///
1316
        /// assert_writeable_eq!(
1317
        ///     formatter.format(&time_zone_full),
1318
        ///     "GMT-06:00"
1319
        /// );
1320
        /// ```
1321
        LocalizedOffsetLong,
1322
        description = "UTC offset, long length",
1323
        length_override = Long,
1324
        sample = "GMT-05:00",
1325
        field = (fields::TimeZone::LocalizedOffset, fields::FieldLength::Four),
18✔
1326
        zone_essentials = yes,
1327
    );
1328

1329
    impl_zone_marker!(
1330
        LocalizedOffsetShort,
1331
        description = "UTC offset, short length",
1332
        length_override = Short,
1333
        sample = "GMT-5",
1334
        field = (fields::TimeZone::LocalizedOffset, fields::FieldLength::One),
16✔
1335
        zone_essentials = yes,
1336
    );
1337

1338
    // TODO: Add short/long UTC offset?
1339

1340
    impl_zone_marker!(
1341
        /// When a display name is unavailable, falls back to the location format:
1342
        ///
1343
        /// ```
1344
        /// use icu::datetime::input::Date;
1345
        /// use icu::datetime::input::{Time, TimeZone};
1346
        /// use icu::calendar::Gregorian;
1347
        /// use icu::datetime::FixedCalendarDateTimeFormatter;
1348
        /// use icu::datetime::fieldsets::zone::GenericShort;
1349
        /// use icu::locale::locale;
1350
        /// use tinystr::tinystr;
1351
        /// use writeable::assert_writeable_eq;
1352
        ///
1353
        /// // Time zone info for Europe/Istanbul
1354
        /// let zone = TimeZone(tinystr!(8, "trist"))
1355
        ///     .without_offset()
1356
        ///     .at_time((Date::try_new_iso(2022, 1, 29).unwrap(), Time::midnight()));
1357
        ///
1358
        /// let fmt = FixedCalendarDateTimeFormatter::<Gregorian, _>::try_new(
1359
        ///     locale!("en").into(),
1360
        ///     GenericShort,
1361
        /// )
1362
        /// .unwrap();
1363
        ///
1364
        /// assert_writeable_eq!(
1365
        ///     fmt.format(&zone),
1366
        ///     "Türkiye Time"
1367
        /// );
1368
        /// ```
1369
        ///
1370
        /// Can also fall back to the UTC offset:
1371
        ///
1372
        /// ```
1373
        /// use icu::datetime::input::Date;
1374
        /// use icu::datetime::input::Time;
1375
        /// use icu::datetime::NoCalendarFormatter;
1376
        /// use icu::datetime::fieldsets::zone::GenericShort;
1377
        /// use icu::datetime::DateTimeWriteError;
1378
        /// use icu::locale::locale;
1379
        /// use icu::time::zone::IanaParser;
1380
        /// use tinystr::tinystr;
1381
        /// use writeable::assert_writeable_eq;
1382
        ///
1383
        /// // Set up the formatter
1384
        /// let mut tzf = NoCalendarFormatter::try_new(
1385
        ///     locale!("en").into(),
1386
        ///     GenericShort,
1387
        /// )
1388
        /// .unwrap();
1389
        ///
1390
        /// // "uschi" - has symbol data for short generic non-location
1391
        /// let time_zone = IanaParser::new()
1392
        ///     .parse("America/Chicago")
1393
        ///     .with_offset("-05".parse().ok())
1394
        ///     .at_time((Date::try_new_iso(2022, 8, 29).unwrap(), Time::midnight()));
1395
        /// assert_writeable_eq!(
1396
        ///     tzf.format(&time_zone),
1397
        ///     "CT"
1398
        /// );
1399
        ///
1400
        /// // "ushnl" - has time zone override symbol data for short generic non-location
1401
        /// let time_zone = IanaParser::new()
1402
        ///     .parse("Pacific/Honolulu")
1403
        ///     .with_offset("-10".parse().ok())
1404
        ///     .at_time((Date::try_new_iso(2022, 8, 29).unwrap(), Time::midnight()));
1405
        /// assert_writeable_eq!(
1406
        ///     tzf.format(&time_zone),
1407
        ///     "HST"
1408
        /// );
1409
        ///
1410
        /// // Mis-spelling of "America/Chicago" results in a fallback to offset format
1411
        /// let time_zone = IanaParser::new()
1412
        ///     .parse("America/Chigagou")
1413
        ///     .with_offset("-05".parse().ok())
1414
        ///     .at_time((Date::try_new_iso(2022, 8, 29).unwrap(), Time::midnight()));
1415
        /// assert_writeable_eq!(
1416
        ///     tzf.format(&time_zone),
1417
        ///     "GMT-5"
1418
        /// );
1419
        /// ```
1420
        ///
1421
        /// Since non-location names might change over time,
1422
        /// this time zone style requires a reference time.
1423
        ///
1424
        /// ```compile_fail,E0271
1425
        /// use icu::datetime::NoCalendarFormatter;
1426
        /// use icu::datetime::fieldsets::zone::GenericLong;
1427
        /// use icu::datetime::input::TimeZone;
1428
        /// use tinystr::tinystr;
1429
        /// use icu::locale::locale;
1430
        /// use writeable::assert_writeable_eq;
1431
        ///
1432
        /// let time_zone_basic = TimeZone(tinystr!(8, "uschi")).without_offset();
1433
        ///
1434
        /// let formatter = NoCalendarFormatter::try_new(
1435
        ///     locale!("en-US").into(),
1436
        ///     GenericLong,
1437
        /// )
1438
        /// .unwrap();
1439
        ///
1440
        /// // error[E0271]: type mismatch resolving `<Base as TimeZoneModel>::LocalTime == (Date<Iso>, Time)`
1441
        /// // note: required by a bound in `NoCalendarFormatter::<C, FSet>::format`
1442
        /// formatter.format(&time_zone_basic);
1443
        /// ```
1444
        GenericLong,
1445
        description = "time zone in generic non-location format, long length",
1446
        length_override = Long,
1447
        sample = "Central Time",
1448
        field = (fields::TimeZone::GenericNonLocation, fields::FieldLength::Four),
14✔
1449
        zone_essentials = yes,
1450
        zone_locations = yes,
1451
        zone_generic_long = yes,
1452
        zone_standard_long = yes,
1453
        metazone_periods = yes,
1454
        input_tzid = yes,
1455
        input_localtime = yes,
1456
    );
1457

1458
    impl_zone_marker!(
1459
        /// Note: short time zones names are usually only available for time zones in the country
1460
        /// associated with a locale (so "PT" is in `en`, but not in `en-GB`). Most time zones will
1461
        /// fall back to the significantly longer location format (e.g. "Los Angeles Time" in `en-GB`).
1462
        ///
1463
        /// Since non-location names might change over time,
1464
        /// this time zone style requires a reference time.
1465
        ///
1466
        /// ```compile_fail,E0271
1467
        /// use icu::datetime::FixedCalendarDateTimeFormatter;
1468
        /// use icu::datetime::fieldsets::zone::GenericShort;
1469
        /// use icu::datetime::input::TimeZone;
1470
        /// use tinystr::tinystr;
1471
        /// use icu::locale::locale;
1472
        /// use writeable::assert_writeable_eq;
1473
        ///
1474
        /// let time_zone_basic = TimeZone(tinystr!(8, "uschi")).with_offset("-06".parse().ok());
1475
        ///
1476
        /// let formatter = FixedCalendarDateTimeFormatter::try_new(
1477
        ///     locale!("en-US").into(),
1478
        ///     GenericShort,
1479
        /// )
1480
        /// .unwrap();
1481
        ///
1482
        /// // error[E0271]: type mismatch resolving `<Base as TimeZoneModel>::LocalTime == (Date<Iso>, Time)`
1483
        /// // note: required by a bound in `FixedCalendarDateTimeFormatter::<C, FSet>::format`
1484
        /// formatter.format(&time_zone_basic);
1485
        /// ```
1486
        GenericShort,
1487
        description = "time zone in generic non-location format, short length",
1488
        length_override = Short,
1489
        sample = "CT",
1490
        field = (fields::TimeZone::GenericNonLocation, fields::FieldLength::One),
21✔
1491
        zone_essentials = yes,
1492
        zone_locations = yes,
1493
        zone_generic_short = yes,
1494
        metazone_periods = yes,
1495
        input_tzid = yes,
1496
        input_localtime = yes,
1497
    );
1498

1499
    impl_zone_marker!(
1500
        /// A time zone ID is required to format with this style.
1501
        /// For example, a raw [`UtcOffset`] cannot be used here.
1502
        ///
1503
        /// ```compile_fail,E0277
1504
        /// use icu::datetime::input::{DateTime, Iso};
1505
        /// use icu::datetime::FixedCalendarDateTimeFormatter;
1506
        /// use icu::datetime::fieldsets::zone::Location;
1507
        /// use icu::datetime::input::UtcOffset;
1508
        /// use tinystr::tinystr;
1509
        /// use icu::locale::locale;
1510
        /// use writeable::assert_writeable_eq;
1511
        ///
1512
        /// let utc_offset = UtcOffset::try_from_str("-06").unwrap();
1513
        ///
1514
        /// let formatter = FixedCalendarDateTimeFormatter::try_new(
1515
        ///     locale!("en-US").into(),
1516
        ///     Location,
1517
        /// )
1518
        /// .unwrap();
1519
        ///
1520
        /// // error[E0277]: the trait bound `UtcOffset: AllInputMarkers<Location>` is not satisfied
1521
        /// // note: required by a bound in `FixedCalendarDateTimeFormatter::<C, FSet>::format`
1522
        /// formatter.format(&utc_offset);
1523
        /// ```
1524
        Location,
1525
        description = "time zone in location format",
1526
        length_override = Long,
1527
        sample = "Chicago Time",
1528
        field = (fields::TimeZone::Location, fields::FieldLength::Four),
16✔
1529
        zone_essentials = yes,
1530
        zone_locations = yes,
1531
        input_tzid = yes,
1532
    );
1533

1534
    impl_zone_marker!(
1535
        /// A time zone ID is required to format with this style.
1536
        /// For example, a raw [`UtcOffset`] cannot be used here.
1537
        ///
1538
        /// ```compile_fail,E0277
1539
        /// use icu::datetime::input::{DateTime, Iso};
1540
        /// use icu::datetime::FixedCalendarDateTimeFormatter;
1541
        /// use icu::datetime::fieldsets::zone::ExemplarCity;
1542
        /// use icu::datetime::input::UtcOffset;
1543
        /// use tinystr::tinystr;
1544
        /// use icu::locale::locale;
1545
        /// use writeable::assert_writeable_eq;
1546
        ///
1547
        /// let utc_offset = UtcOffset::try_from_str("-06").unwrap();
1548
        ///
1549
        /// let formatter = FixedCalendarDateTimeFormatter::try_new(
1550
        ///     locale!("en-US").into(),
1551
        ///     ExemplarCity,
1552
        /// )
1553
        /// .unwrap();
1554
        ///
1555
        /// // error[E0277]: the trait bound `UtcOffset: AllInputMarkers<ExemplarCity>` is not satisfied
1556
        /// // note: required by a bound in `FixedCalendarDateTimeFormatter::<C, FSet>::format`
1557
        /// formatter.format(&utc_offset);
1558
        /// ```
1559
        ExemplarCity,
1560
        description = "time zone in exemplar city format",
1561
        length_override = Long,
1562
        sample = "Chicago",
1563
        field = (fields::TimeZone::Location, fields::FieldLength::Three),
14✔
1564
        zone_locations = yes,
1565
        zone_exemplars = yes,
1566
        input_tzid = yes,
1567
    );
1568
}
1569

1570
impl_zone_combo_helpers!(DateFieldSet, DateZone, DateFieldSet);
1571

1572
impl_zone_combo_helpers!(TimeFieldSet, TimeZone, TimeFieldSet);
1573

1574
impl_zone_combo_helpers!(DateAndTimeFieldSet, DateTimeZone, DateAndTimeFieldSet);
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