icu_datetime/
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//! There are 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"]
53pub mod builder;
54#[path = "dynamic.rs"]
55pub mod enums;
56
57pub use crate::combo::Combo;
58
59use crate::{
60    options::*,
61    provider::{neo::*, time_zones::tz, *},
62    raw::neo::RawOptions,
63    scaffold::*,
64};
65use enums::*;
66use icu_calendar::types::{DayOfMonth, MonthInfo, Weekday, YearInfo};
67use icu_provider::marker::NeverMarker;
68use icu_time::{
69    zone::{TimeZoneVariant, UtcOffset, ZoneNameTimestamp},
70    Hour, Minute, Nanosecond, Second, TimeZone,
71};
72
73#[cfg(doc)]
74use icu_time::TimeZoneInfo;
75
76/// Maps the token `yes` to the given ident
77macro_rules! yes_to {
78    ($any:expr, $nonempty:expr) => {
79        $any
80    };
81}
82
83macro_rules! yes_or {
84    ($fallback:expr, $actual:expr) => {
85        $actual
86    };
87    ($fallback:expr,) => {
88        $fallback
89    };
90}
91
92macro_rules! ternary {
93    ($present:expr, $missing:expr, yes) => {
94        $present
95    };
96    ($present:expr, $missing:expr, $any:literal) => {
97        $present
98    };
99    ($present:expr, $missing:expr,) => {
100        $missing
101    };
102}
103
104/// Generates the options argument passed into the docs test constructor
105macro_rules! length_option_helper {
106    ($type:ty, $length:ident) => {
107        concat!(stringify!($type), "::", stringify!($length), "()")
108    };
109}
110
111macro_rules! impl_composite {
112    ($type:ident, $variant:ident, $enum:ident) => {
113        impl $type {
114            #[inline]
115            pub(crate) fn to_enum(self) -> $enum {
116                $enum::$type(self)
117            }
118        }
119        impl GetField<CompositeFieldSet> for $type {
120            #[inline]
121            fn get_field(&self) -> CompositeFieldSet {
122                CompositeFieldSet::$variant(self.to_enum())
123            }
124        }
125    };
126}
127
128macro_rules! impl_marker_length_constructors {
129    (
130        $type:ident,
131        $(alignment: $alignment_yes:ident,)?
132        $(year_style: $yearstyle_yes:ident,)?
133        $(time_precision: $timeprecision_yes:ident,)?
134    ) => {
135        impl $type {
136            #[doc = concat!("Creates a ", stringify!($type), " skeleton with the given formatting length.")]
137            pub const fn for_length(length: Length) -> Self {
138                Self {
139                    length,
140                    $(
141                        alignment: yes_to!(None, $alignment_yes),
142                    )?
143                    $(
144                        year_style: yes_to!(None, $yearstyle_yes),
145                    )?
146                    $(
147                        time_precision: yes_to!(None, $timeprecision_yes),
148                    )?
149                }
150            }
151            #[doc = concat!("Creates a ", stringify!($type), " skeleton with a long length.")]
152            pub const fn long() -> Self {
153                Self::for_length(Length::Long)
154            }
155            #[doc = concat!("Creates a ", stringify!($type), " skeleton with a medium length.")]
156            pub const fn medium() -> Self {
157                Self::for_length(Length::Medium)
158            }
159            #[doc = concat!("Creates a ", stringify!($type), " skeleton with a short length.")]
160            pub const fn short() -> Self {
161                Self::for_length(Length::Short)
162            }
163        }
164    };
165}
166
167macro_rules! impl_time_precision_constructors {
168    (
169        $time_type:ident,
170    ) => {
171        impl $time_type {
172            #[doc = concat!("Creates a ", stringify!($type), " that formats hours and minutes with the default length.")]
173            pub fn hm() -> Self {
174                Self::for_length(Default::default()).with_time_precision(TimePrecision::Minute)
175            }
176            #[doc = concat!("Creates a ", stringify!($type), " that formats hours, minutes, and seconds with the default length.")]
177            pub fn hms() -> Self {
178                Self::for_length(Default::default()).with_time_precision(TimePrecision::Second)
179            }
180            #[doc = concat!("Creates a ", stringify!($type), " that formats hours, minutes, seconds, and subseconds with the default length.")]
181            pub fn hmss(subsecond_digits: SubsecondDigits) -> Self {
182                Self::for_length(Default::default()).with_time_precision(TimePrecision::Subsecond(subsecond_digits))
183            }
184        }
185    };
186}
187
188macro_rules! impl_marker_with_options {
189    (
190        $(#[$attr:meta])*
191        $type:ident,
192        $(sample_length: $sample_length:ident,)?
193        $(date_fields: $date_fields:expr,)?
194        $(alignment: $alignment_yes:ident,)?
195        $(year_style: $yearstyle_yes:ident,)?
196        $(time_precision: $timeprecision_yes:ident,)?
197        $(length_override: $length_override:ident,)?
198    ) => {
199        $(#[$attr])*
200        #[derive(Debug, Copy, Clone, PartialEq, Eq)]
201        #[non_exhaustive]
202        pub struct $type {
203            $(
204                /// The desired length of the formatted string.
205                ///
206                /// See: [`Length`]
207                pub length: datetime_marker_helper!(@option/length, $sample_length),
208            )?
209            $(
210                /// Whether fields should be aligned for a column-like layout.
211                ///
212                /// See: [`Alignment`]
213                pub alignment: datetime_marker_helper!(@option/alignment, $alignment_yes),
214            )?
215            $(
216                /// When to display the era field in the formatted string.
217                ///
218                /// See: [`YearStyle`]
219                pub year_style: datetime_marker_helper!(@option/yearstyle, $yearstyle_yes),
220            )?
221            $(
222                /// How precisely to display the time of day
223                ///
224                /// See: [`TimePrecision`]
225                pub time_precision: datetime_marker_helper!(@option/timeprecision, $timeprecision_yes),
226            )?
227        }
228        impl $type {
229            pub(crate) fn to_raw_options(self) -> RawOptions {
230                RawOptions {
231                    length: yes_or!(Some(self.length), $(Some(Length::$length_override))?),
232                    date_fields: yes_or!(None, $($date_fields)?),
233                    alignment: ternary!(self.alignment, None, $($alignment_yes)?),
234                    year_style: ternary!(self.year_style, None, $($yearstyle_yes)?),
235                    time_precision: ternary!(self.time_precision, None, $($timeprecision_yes)?),
236                }
237            }
238            /// Builds this field set, removing the needed options from the builder.
239            pub(crate) fn take_from_builder(
240                options: &mut builder::FieldSetBuilder
241            ) -> Self {
242                Self {
243                    $(length: yes_to!(options.length.take().unwrap_or_default(), $sample_length),)?
244                    $(alignment: yes_to!(options.alignment.take(), $alignment_yes),)?
245                    $(year_style: yes_to!(options.year_style.take(), $yearstyle_yes),)?
246                    $(time_precision: yes_to!(options.time_precision.take(), $timeprecision_yes),)?
247                }
248            }
249        }
250        $(
251            impl $type {
252                /// Sets the length option.
253                pub const fn with_length(mut self, length: Length) -> Self {
254                    self.length = yes_to!(length, $sample_length);
255                    self
256                }
257            }
258        )?
259        $(
260            impl $type {
261                /// Sets the alignment option.
262                pub const fn with_alignment(mut self, alignment: Alignment) -> Self {
263                    self.alignment = Some(yes_to!(alignment, $alignment_yes));
264                    self
265                }
266            }
267        )?
268        $(
269            impl $type {
270                /// Sets the year style option.
271                pub const fn with_year_style(mut self, year_style: YearStyle) -> Self {
272                    self.year_style = Some(yes_to!(year_style, $yearstyle_yes));
273                    self
274                }
275            }
276        )?
277        $(
278            impl $type {
279                /// Sets the time precision option.
280                pub const fn with_time_precision(mut self, time_precision: TimePrecision) -> Self {
281                    self.time_precision = Some(yes_to!(time_precision, $timeprecision_yes));
282                    self
283                }
284            }
285        )?
286    };
287}
288
289macro_rules! impl_date_to_time_helpers {
290    (
291        $type:ident,
292        $type_time:ident,
293        $(alignment = $alignment_yes:ident,)?
294        $(year_style = $yearstyle_yes:ident,)?
295    ) => {
296        impl $type {
297            /// Associates this field set with a time precision.
298            pub fn with_time(self, time_precision: TimePrecision) -> $type_time {
299                $type_time {
300                    length: self.length,
301                    time_precision: Some(time_precision),
302                    alignment: ternary!(self.alignment, Default::default(), $($alignment_yes)?),
303                    $(year_style: yes_to!(self.year_style, $yearstyle_yes),)?
304                }
305            }
306            /// Shorthand to associate this field set with [`TimePrecision::Minute`].
307            ///
308            /// # Examples
309            ///
310            /// ```
311            #[doc = concat!("use icu::datetime::fieldsets::", stringify!($type), ";")]
312            /// use icu::datetime::options::TimePrecision;
313            ///
314            #[doc = concat!("let fs1 = ", stringify!($type), "::medium().with_time(TimePrecision::Minute);")]
315            #[doc = concat!("let fs2 = ", stringify!($type), "::medium().with_time_hm();")]
316            /// assert_eq!(fs1, fs2);
317            /// ```
318            pub fn with_time_hm(self) -> $type_time {
319                $type_time {
320                    length: self.length,
321                    time_precision: Some(TimePrecision::Minute),
322                    alignment: ternary!(self.alignment, Default::default(), $($alignment_yes)?),
323                    $(year_style: yes_to!(self.year_style, $yearstyle_yes),)?
324                }
325            }
326            /// Shorthand to associate this field set with [`TimePrecision::Second`].
327            ///
328            /// # Examples
329            ///
330            /// ```
331            #[doc = concat!("use icu::datetime::fieldsets::", stringify!($type), ";")]
332            /// use icu::datetime::options::TimePrecision;
333            ///
334            #[doc = concat!("let fs1 = ", stringify!($type), "::medium().with_time(TimePrecision::Second);")]
335            #[doc = concat!("let fs2 = ", stringify!($type), "::medium().with_time_hms();")]
336            /// assert_eq!(fs1, fs2);
337            /// ```
338            pub fn with_time_hms(self) -> $type_time {
339                $type_time {
340                    length: self.length,
341                    time_precision: Some(TimePrecision::Second),
342                    alignment: ternary!(self.alignment, Default::default(), $($alignment_yes)?),
343                    $(year_style: yes_to!(self.year_style, $yearstyle_yes),)?
344                }
345            }
346            /// Shorthand to associate this field set with [`TimePrecision::Subsecond`].
347            ///
348            /// # Examples
349            ///
350            /// ```
351            #[doc = concat!("use icu::datetime::fieldsets::", stringify!($type), ";")]
352            /// use icu::datetime::options::TimePrecision;
353            /// use icu::datetime::options::SubsecondDigits::S2;
354            ///
355            #[doc = concat!("let fs1 = ", stringify!($type), "::medium().with_time(TimePrecision::Subsecond(S2));")]
356            #[doc = concat!("let fs2 = ", stringify!($type), "::medium().with_time_hmss(S2);")]
357            /// assert_eq!(fs1, fs2);
358            /// ```
359            pub fn with_time_hmss(self, subsecond_digits: SubsecondDigits) -> $type_time {
360                $type_time {
361                    length: self.length,
362                    time_precision: Some(TimePrecision::Subsecond(subsecond_digits)),
363                    alignment: ternary!(self.alignment, Default::default(), $($alignment_yes)?),
364                    $(year_style: yes_to!(self.year_style, $yearstyle_yes),)?
365                }
366            }
367        }
368    };
369}
370
371macro_rules! impl_combo_get_field {
372    ($type:ident, $composite:ident, $enum:ident, $variant:path) => {
373        impl GetField<CompositeFieldSet> for Combo<$type, $variant> {
374            #[inline]
375            fn get_field(&self) -> CompositeFieldSet {
376                CompositeFieldSet::$composite(Combo::new(self.dt().to_enum(), self.z().to_enum()))
377            }
378        }
379        impl Combo<$type, $variant> {
380            /// Convert this specific [`Combo`] into a more general [`Combo`].
381            /// Useful when adding to the field of a [`CompositeFieldSet`].
382            ///
383            /// [`CompositeFieldSet`]: enums::CompositeFieldSet
384            pub fn into_enums(self) -> Combo<$enum, ZoneFieldSet> {
385                Combo::new(self.dt().to_enum(), self.z().to_enum())
386            }
387        }
388    };
389}
390
391macro_rules! impl_zone_combo_helpers {
392    (
393        $type:ident,
394        $composite:ident,
395        $enum:ident
396    ) => {
397        impl $type {
398            #[inline]
399            /// Associates this field set with a time zone field set.
400            pub fn with_zone<Z: ZoneMarkers>(self, zone: Z) -> Combo<Self, Z> {
401                Combo::new(self, zone)
402            }
403        }
404        impl_combo_get_field!($type, $composite, $enum, zone::SpecificLong);
405        impl_combo_get_field!($type, $composite, $enum, zone::SpecificShort);
406        impl_combo_get_field!($type, $composite, $enum, zone::LocalizedOffsetLong);
407        impl_combo_get_field!($type, $composite, $enum, zone::LocalizedOffsetShort);
408        impl_combo_get_field!($type, $composite, $enum, zone::GenericLong);
409        impl_combo_get_field!($type, $composite, $enum, zone::GenericShort);
410        impl_combo_get_field!($type, $composite, $enum, zone::Location);
411        impl_combo_get_field!($type, $composite, $enum, zone::ExemplarCity);
412        impl_combo_get_field!($type, $composite, $enum, ZoneFieldSet);
413    };
414}
415
416/// Internal helper macro used by [`impl_date_marker`] and [`impl_calendar_period_marker`]
417macro_rules! impl_date_or_calendar_period_marker {
418    (
419        $(#[$attr:meta])*
420        // The name of the type being created.
421        $type:ident,
422        // A plain language description of the field set for documentation.
423        description = $description:literal,
424        // Length of the sample string below.
425        sample_length = $sample_length:ident,
426        // A sample string. A docs test will be generated!
427        sample = $sample:literal,
428        // Whether years can occur.
429        $(years = $years_yes:ident,)?
430        // Whether months can occur.
431        $(months = $months_yes:ident,)?
432        // Whether weekdays can occur.
433        $(weekdays = $weekdays_yes:ident,)?
434        // Whether the input should contain years.
435        $(input_year = $year_yes:ident,)?
436        // Whether the input should contain months.
437        $(input_month = $month_yes:ident,)?
438        // Whether the input should contain the day of the month.
439        $(input_day_of_month = $day_of_month_yes:ident,)?
440        // Whether the input should contain the day of the week.
441        $(input_day_of_week = $day_of_week_yes:ident,)?
442        // Whether the input should contain the day of the year.
443        $(input_day_of_year = $day_of_year_yes:ident,)?
444        // Whether the input should declare its calendar kind.
445        $(input_any_calendar_kind = $any_calendar_kind_yes:ident,)?
446        // Whether the alignment option should be available.
447        // According to UTS 35, it should be available with years, months, and days.
448        $(option_alignment = $option_alignment_yes:ident,)?
449    ) => {
450        impl_marker_with_options!(
451            #[doc = concat!("**“", $sample, "**” ⇒ ", $description)]
452            ///
453            /// This is a field set marker. For more information, see [`fieldsets`](crate::fieldsets).
454            ///
455            /// # Examples
456            ///
457            /// In [`DateTimeFormatter`](crate::neo::DateTimeFormatter):
458            ///
459            /// ```
460            /// use icu::datetime::input::Date;
461            /// use icu::datetime::DateTimeFormatter;
462            #[doc = concat!("use icu::datetime::fieldsets::", stringify!($type), ";")]
463            /// use icu::locale::locale;
464            /// use writeable::assert_writeable_eq;
465            #[doc = concat!("let fmt = DateTimeFormatter::<", stringify!($type), ">::try_new(")]
466            ///     locale!("en").into(),
467            #[doc = concat!("    ", length_option_helper!($type, $sample_length), ",")]
468            /// )
469            /// .unwrap();
470            /// let dt = Date::try_new_iso(2024, 5, 17).unwrap();
471            ///
472            /// assert_writeable_eq!(
473            ///     fmt.format(&dt),
474            #[doc = concat!("    \"", $sample, "\"")]
475            /// );
476            /// ```
477            ///
478            /// In [`FixedCalendarDateTimeFormatter`](crate::neo::FixedCalendarDateTimeFormatter):
479            ///
480            /// ```
481            /// use icu::datetime::input::Date;
482            /// use icu::calendar::Gregorian;
483            /// use icu::datetime::FixedCalendarDateTimeFormatter;
484            #[doc = concat!("use icu::datetime::fieldsets::", stringify!($type), ";")]
485            /// use icu::locale::locale;
486            /// use writeable::assert_writeable_eq;
487            ///
488            #[doc = concat!("let fmt = FixedCalendarDateTimeFormatter::<Gregorian, ", stringify!($type), ">::try_new(")]
489            ///     locale!("en").into(),
490            #[doc = concat!("    ", length_option_helper!($type, $sample_length), ",")]
491            /// )
492            /// .unwrap();
493            /// let dt = Date::try_new_gregorian(2024, 5, 17).unwrap();
494            ///
495            /// assert_writeable_eq!(
496            ///     fmt.format(&dt),
497            #[doc = concat!("    \"", $sample, "\"")]
498            /// );
499            /// ```
500            $(#[$attr])*
501            $type,
502            sample_length: $sample_length,
503            date_fields: Some(builder::DateFields::$type),
504            $(alignment: $option_alignment_yes,)?
505            $(year_style: $year_yes,)?
506        );
507        impl_marker_length_constructors!(
508            $type,
509            $(alignment: $option_alignment_yes,)?
510            $(year_style: $year_yes,)?
511        );
512        impl UnstableSealed for $type {}
513        impl DateTimeNamesMarker for $type {
514            type YearNames = datetime_marker_helper!(@names/year, $($years_yes)?);
515            type MonthNames = datetime_marker_helper!(@names/month, $($months_yes)?);
516            type WeekdayNames = datetime_marker_helper!(@names/weekday, $($weekdays_yes)?);
517            type DayPeriodNames = datetime_marker_helper!(@names/dayperiod,);
518            type ZoneEssentials = datetime_marker_helper!(@names/zone/essentials,);
519            type ZoneLocations = datetime_marker_helper!(@names/zone/locations,);
520            type ZoneLocationsRoot = datetime_marker_helper!(@names/zone/locations_root,);
521            type ZoneExemplars = datetime_marker_helper!(@names/zone/exemplar,);
522            type ZoneExemplarsRoot = datetime_marker_helper!(@names/zone/exemplar_root,);
523            type ZoneGenericLong = datetime_marker_helper!(@names/zone/generic_long,);
524            type ZoneGenericShort = datetime_marker_helper!(@names/zone/generic_short,);
525            type ZoneStandardLong = datetime_marker_helper!(@names/zone/standard_long,);
526            type ZoneSpecificLong = datetime_marker_helper!(@names/zone/specific_long,);
527            type ZoneSpecificShort = datetime_marker_helper!(@names/zone/specific_short,);
528            type MetazoneLookup = datetime_marker_helper!(@names/zone/metazone_periods,);
529        }
530        impl DateInputMarkers for $type {
531            type YearInput = datetime_marker_helper!(@input/year, $($year_yes)?);
532            type MonthInput = datetime_marker_helper!(@input/month, $($month_yes)?);
533            type DayOfMonthInput = datetime_marker_helper!(@input/day_of_month, $($day_of_month_yes)?);
534            type DayOfYearInput = datetime_marker_helper!(@input/day_of_year, $($day_of_year_yes)?);
535            type DayOfWeekInput = datetime_marker_helper!(@input/day_of_week, $($day_of_week_yes)?);
536        }
537        impl<C: CldrCalendar> TypedDateDataMarkers<C> for $type {
538            type DateSkeletonPatternsV1 = datetime_marker_helper!(@dates/typed, yes);
539            type YearNamesV1 = datetime_marker_helper!(@years/typed, $($years_yes)?);
540            type MonthNamesV1 = datetime_marker_helper!(@months/typed, $($months_yes)?);
541            type WeekdayNamesV1 = datetime_marker_helper!(@weekdays, $($weekdays_yes)?);
542        }
543        impl DateDataMarkers for $type {
544            type Skel = datetime_marker_helper!(@calmarkers, yes);
545            type Year = datetime_marker_helper!(@calmarkers, $($years_yes)?);
546            type Month = datetime_marker_helper!(@calmarkers, $($months_yes)?);
547            type WeekdayNamesV1 = datetime_marker_helper!(@weekdays, $($weekdays_yes)?);
548        }
549        impl DateTimeMarkers for $type {
550            type D = Self;
551            type T = ();
552            type Z = ();
553            type GluePatternV1 = datetime_marker_helper!(@glue,);
554        }
555    };
556}
557
558/// Implements a field set of date fields.
559///
560/// Several arguments to this macro are required, and the rest are optional.
561/// The optional arguments should be written as `key = yes,` if that parameter
562/// should be included.
563///
564/// See [`impl_date_marker`].
565macro_rules! impl_date_marker {
566    (
567        $(#[$attr:meta])*
568        $type:ident,
569        $type_time:ident,
570        description = $description:literal,
571        sample_length = $sample_length:ident,
572        sample = $sample:literal,
573        sample_time = $sample_time:literal,
574        $(years = $years_yes:ident,)?
575        $(months = $months_yes:ident,)?
576        $(dates = $dates_yes:ident,)?
577        $(weekdays = $weekdays_yes:ident,)?
578        $(input_year = $year_yes:ident,)?
579        $(input_month = $month_yes:ident,)?
580        $(input_day_of_month = $day_of_month_yes:ident,)?
581        $(input_day_of_week = $day_of_week_yes:ident,)?
582        $(input_day_of_year = $day_of_year_yes:ident,)?
583        $(input_any_calendar_kind = $any_calendar_kind_yes:ident,)?
584        $(option_alignment = $option_alignment_yes:ident,)?
585    ) => {
586        impl_date_or_calendar_period_marker!(
587            $(#[$attr])*
588            $type,
589            description = $description,
590            sample_length = $sample_length,
591            sample = $sample,
592            $(years = $years_yes,)?
593            $(months = $months_yes,)?
594            $(dates = $dates_yes,)?
595            $(weekdays = $weekdays_yes,)?
596            $(input_year = $year_yes,)?
597            $(input_month = $month_yes,)?
598            $(input_day_of_month = $day_of_month_yes,)?
599            $(input_day_of_week = $day_of_week_yes,)?
600            $(input_day_of_year = $day_of_year_yes,)?
601            $(input_any_calendar_kind = $any_calendar_kind_yes,)?
602            $(option_alignment = $option_alignment_yes,)?
603        );
604        impl_zone_combo_helpers!($type, DateZone, DateFieldSet);
605        impl_composite!($type, Date, DateFieldSet);
606        impl_date_to_time_helpers!($type, $type_time, $(alignment = $option_alignment_yes,)? $(year_style = $year_yes,)?);
607        impl_marker_with_options!(
608            #[doc = concat!("**“", $sample_time, "**” ⇒ ", $description, " with time")]
609            ///
610            /// # Examples
611            ///
612            /// In [`DateTimeFormatter`](crate::neo::DateTimeFormatter):
613            ///
614            /// ```
615            /// use icu::datetime::input::Date;
616            /// use icu::datetime::DateTimeFormatter;
617            #[doc = concat!("use icu::datetime::fieldsets::", stringify!($type_time), ";")]
618            /// use icu::locale::locale;
619            /// use icu::datetime::input::{DateTime, Time};
620            /// use writeable::assert_writeable_eq;
621            ///
622            #[doc = concat!("let fmt = DateTimeFormatter::try_new(")]
623            ///     locale!("en").into(),
624            #[doc = concat!("    ", length_option_helper!($type_time, $sample_length), ",")]
625            /// )
626            /// .unwrap();
627            /// let dt = DateTime { date: Date::try_new_iso(2024, 5, 17).unwrap(), time: Time::try_new(15, 47, 50, 0).unwrap() };
628            ///
629            /// assert_writeable_eq!(
630            ///     fmt.format(&dt),
631            #[doc = concat!("    \"", $sample_time, "\"")]
632            /// );
633            /// ```
634            ///
635            /// In [`FixedCalendarDateTimeFormatter`](crate::neo::FixedCalendarDateTimeFormatter):
636            ///
637            /// ```
638            /// use icu::datetime::input::Date;
639            /// use icu::calendar::Gregorian;
640            /// use icu::datetime::FixedCalendarDateTimeFormatter;
641            #[doc = concat!("use icu::datetime::fieldsets::", stringify!($type_time), ";")]
642            /// use icu::locale::locale;
643            /// use icu::datetime::input::{DateTime, Time};
644            /// use writeable::assert_writeable_eq;
645            ///
646            #[doc = concat!("let fmt = FixedCalendarDateTimeFormatter::try_new(")]
647            ///     locale!("en").into(),
648            #[doc = concat!("    ", length_option_helper!($type_time, $sample_length), ",")]
649            /// )
650            /// .unwrap();
651            /// let dt = DateTime { date: Date::try_new_gregorian(2024, 5, 17).unwrap(), time: Time::try_new(15, 47, 50, 0).unwrap() };
652            ///
653            /// assert_writeable_eq!(
654            ///     fmt.format(&dt),
655            #[doc = concat!("    \"", $sample_time, "\"")]
656            /// );
657            /// ```
658            $(#[$attr])*
659            $type_time,
660            sample_length: $sample_length,
661            date_fields: Some(builder::DateFields::$type),
662            alignment: yes,
663            $(year_style: $year_yes,)?
664            time_precision: yes,
665        );
666        impl_marker_length_constructors!(
667            $type_time,
668            alignment: yes,
669            $(year_style: $year_yes,)?
670            time_precision: yes,
671        );
672        impl_zone_combo_helpers!($type_time, DateTimeZone, DateAndTimeFieldSet);
673        impl UnstableSealed for $type_time {}
674        impl DateTimeNamesMarker for $type_time {
675            type YearNames = datetime_marker_helper!(@names/year, $($years_yes)?);
676            type MonthNames = datetime_marker_helper!(@names/month, $($months_yes)?);
677            type WeekdayNames = datetime_marker_helper!(@names/weekday, $($weekdays_yes)?);
678            type DayPeriodNames = datetime_marker_helper!(@names/dayperiod, yes);
679            type ZoneEssentials = datetime_marker_helper!(@names/zone/essentials,);
680            type ZoneLocations = datetime_marker_helper!(@names/zone/locations,);
681            type ZoneLocationsRoot = datetime_marker_helper!(@names/zone/locations_root,);
682            type ZoneExemplars = datetime_marker_helper!(@names/zone/exemplar,);
683            type ZoneExemplarsRoot = datetime_marker_helper!(@names/zone/exemplar_root,);
684            type ZoneGenericLong = datetime_marker_helper!(@names/zone/generic_long,);
685            type ZoneGenericShort = datetime_marker_helper!(@names/zone/generic_short,);
686            type ZoneStandardLong = datetime_marker_helper!(@names/zone/standard_long,);
687            type ZoneSpecificLong = datetime_marker_helper!(@names/zone/specific_long,);
688            type ZoneSpecificShort = datetime_marker_helper!(@names/zone/specific_short,);
689            type MetazoneLookup = datetime_marker_helper!(@names/zone/metazone_periods,);
690        }
691        impl DateInputMarkers for $type_time {
692            type YearInput = datetime_marker_helper!(@input/year, $($year_yes)?);
693            type MonthInput = datetime_marker_helper!(@input/month, $($month_yes)?);
694            type DayOfMonthInput = datetime_marker_helper!(@input/day_of_month, $($day_of_month_yes)?);
695            type DayOfYearInput = datetime_marker_helper!(@input/day_of_year, $($day_of_year_yes)?);
696            type DayOfWeekInput = datetime_marker_helper!(@input/day_of_week, $($day_of_week_yes)?);
697        }
698        impl<C: CldrCalendar> TypedDateDataMarkers<C> for $type_time {
699            type DateSkeletonPatternsV1 = datetime_marker_helper!(@dates/typed, yes);
700            type YearNamesV1 = datetime_marker_helper!(@years/typed, $($years_yes)?);
701            type MonthNamesV1 = datetime_marker_helper!(@months/typed, $($months_yes)?);
702            type WeekdayNamesV1 = datetime_marker_helper!(@weekdays, $($weekdays_yes)?);
703        }
704        impl DateDataMarkers for $type_time {
705            type Skel = datetime_marker_helper!(@calmarkers, yes);
706            type Year = datetime_marker_helper!(@calmarkers, $($years_yes)?);
707            type Month = datetime_marker_helper!(@calmarkers, $($months_yes)?);
708            type WeekdayNamesV1 = datetime_marker_helper!(@weekdays, $($weekdays_yes)?);
709        }
710        impl TimeMarkers for $type_time {
711            // TODO(#6497): Consider making dayperiods optional
712            type DayPeriodNamesV1 = datetime_marker_helper!(@dayperiods, yes);
713            type TimeSkeletonPatternsV1 = datetime_marker_helper!(@times, yes);
714            type HourInput = datetime_marker_helper!(@input/hour, yes);
715            type MinuteInput = datetime_marker_helper!(@input/minute, yes);
716            type SecondInput = datetime_marker_helper!(@input/second, yes);
717            type NanosecondInput = datetime_marker_helper!(@input/Nanosecond, yes);
718        }
719        impl DateTimeMarkers for $type_time {
720            type D = Self;
721            type T = Self;
722            type Z = ();
723            type GluePatternV1 = datetime_marker_helper!(@glue, yes);
724        }
725        impl_composite!($type_time, DateTime, DateAndTimeFieldSet);
726        impl $type_time {
727            pub(crate) fn to_date_field_set(self) -> $type {
728                $type {
729                    length: self.length,
730                    $(alignment: yes_to!(self.alignment, $option_alignment_yes),)?
731                    $(year_style: yes_to!(self.year_style, $years_yes),)?
732                }
733            }
734        }
735    };
736}
737
738/// Implements a field set of calendar period fields.
739///
740/// Several arguments to this macro are required, and the rest are optional.
741/// The optional arguments should be written as `key = yes,` if that parameter
742/// should be included.
743///
744/// See [`impl_date_marker`].
745macro_rules! impl_calendar_period_marker {
746    (
747        $(#[$attr:meta])*
748        $type:ident,
749        description = $description:literal,
750        sample_length = $sample_length:ident,
751        sample = $sample:literal,
752        $(years = $years_yes:ident,)?
753        $(months = $months_yes:ident,)?
754        $(dates = $dates_yes:ident,)?
755        $(input_year = $year_yes:ident,)?
756        $(input_month = $month_yes:ident,)?
757        $(input_any_calendar_kind = $any_calendar_kind_yes:ident,)?
758        $(option_alignment = $option_alignment_yes:ident,)?
759    ) => {
760        impl_date_or_calendar_period_marker!(
761            $(#[$attr])*
762            $type,
763            description = $description,
764            sample_length = $sample_length,
765            sample = $sample,
766            $(years = $years_yes,)?
767            $(months = $months_yes,)?
768            $(dates = $dates_yes,)?
769            $(input_year = $year_yes,)?
770            $(input_month = $month_yes,)?
771            $(input_any_calendar_kind = $any_calendar_kind_yes,)?
772            $(option_alignment = $option_alignment_yes,)?
773        );
774        impl_composite!($type, CalendarPeriod, CalendarPeriodFieldSet);
775    };
776}
777
778/// Implements a field set of time fields.
779///
780/// Several arguments to this macro are required, and the rest are optional.
781/// The optional arguments should be written as `key = yes,` if that parameter
782/// should be included.
783///
784/// Documentation for each option is shown inline below.
785macro_rules! impl_time_marker {
786    (
787        $(#[$attr:meta])*
788        // The name of the type being created.
789        $type:ident,
790        // A plain language description of the field set for documentation.
791        description = $description:literal,
792        // Length of the sample string below.
793        sample_length = $sample_length:ident,
794        // A sample string. A docs test will be generated!
795        sample = $sample:literal,
796        // Whether day periods can occur.
797        $(dayperiods = $dayperiods_yes:ident,)?
798        // Whether the input should include hours.
799        $(input_hour = $hour_yes:ident,)?
800        // Whether the input should contain minutes.
801        $(input_minute = $minute_yes:ident,)?
802        // Whether the input should contain seconds.
803        $(input_second = $second_yes:ident,)?
804        // Whether the input should contain fractional seconds.
805        $(input_subsecond = $Nanosecond_yes:ident,)?
806    ) => {
807        impl_marker_with_options!(
808            #[doc = concat!("**“", $sample, "**” ⇒ ", $description)]
809            ///
810            /// # Examples
811            ///
812            /// ```
813            /// use icu::datetime::input::Time;
814            /// use icu::datetime::NoCalendarFormatter;
815            #[doc = concat!("use icu::datetime::fieldsets::", stringify!($type), ";")]
816            /// use icu::locale::locale;
817            /// use writeable::assert_writeable_eq;
818            ///
819            #[doc = concat!("let fmt = NoCalendarFormatter::try_new(")]
820            ///     locale!("en").into(),
821            #[doc = concat!("    ", length_option_helper!($type, $sample_length), ",")]
822            /// )
823            /// .unwrap();
824            /// let time = Time::try_new(15, 47, 50, 0).unwrap();
825            ///
826            /// assert_writeable_eq!(
827            ///     fmt.format(&time),
828            #[doc = concat!("    \"", $sample, "\"")]
829            /// );
830            /// ```
831            $(#[$attr])*
832            $type,
833            sample_length: $sample_length,
834            alignment: yes,
835            time_precision: yes,
836        );
837        impl_marker_length_constructors!(
838            $type,
839            alignment: yes,
840            time_precision: yes,
841        );
842        impl_time_precision_constructors!(
843            $type,
844        );
845        impl_zone_combo_helpers!($type, TimeZone, TimeFieldSet);
846        impl UnstableSealed for $type {}
847        impl DateTimeNamesMarker for $type {
848            type YearNames = datetime_marker_helper!(@names/year,);
849            type MonthNames = datetime_marker_helper!(@names/month,);
850            type WeekdayNames = datetime_marker_helper!(@names/weekday,);
851            type DayPeriodNames = datetime_marker_helper!(@names/dayperiod, $($dayperiods_yes)?);
852            type ZoneEssentials = datetime_marker_helper!(@names/zone/essentials,);
853            type ZoneLocations = datetime_marker_helper!(@names/zone/locations,);
854            type ZoneLocationsRoot = datetime_marker_helper!(@names/zone/locations_root,);
855            type ZoneExemplars = datetime_marker_helper!(@names/zone/exemplar,);
856            type ZoneExemplarsRoot = datetime_marker_helper!(@names/zone/exemplar_root,);
857            type ZoneGenericLong = datetime_marker_helper!(@names/zone/generic_long,);
858            type ZoneGenericShort = datetime_marker_helper!(@names/zone/generic_short,);
859            type ZoneStandardLong = datetime_marker_helper!(@names/zone/standard_long,);
860            type ZoneSpecificLong = datetime_marker_helper!(@names/zone/specific_long,);
861            type ZoneSpecificShort = datetime_marker_helper!(@names/zone/specific_short,);
862            type MetazoneLookup = datetime_marker_helper!(@names/zone/metazone_periods,);
863        }
864        impl TimeMarkers for $type {
865            type DayPeriodNamesV1 = datetime_marker_helper!(@dayperiods, $($dayperiods_yes)?);
866            type TimeSkeletonPatternsV1 = datetime_marker_helper!(@times, yes);
867            type HourInput = datetime_marker_helper!(@input/hour, $($hour_yes)?);
868            type MinuteInput = datetime_marker_helper!(@input/minute, $($minute_yes)?);
869            type SecondInput = datetime_marker_helper!(@input/second, $($second_yes)?);
870            type NanosecondInput = datetime_marker_helper!(@input/Nanosecond, $($Nanosecond_yes)?);
871        }
872        impl DateTimeMarkers for $type {
873            type D = ();
874            type T = Self;
875            type Z = ();
876            type GluePatternV1 = datetime_marker_helper!(@glue,);
877        }
878        impl_composite!($type, Time, TimeFieldSet);
879    };
880}
881
882/// Implements a field set of time zone fields.
883///
884/// Several arguments to this macro are required, and the rest are optional.
885/// The optional arguments should be written as `key = yes,` if that parameter
886/// should be included.
887///
888/// Documentation for each option is shown inline below.
889macro_rules! impl_zone_marker {
890    (
891        $(#[$attr:meta])*
892        // The name of the type being created.
893        $type:ident,
894        // A plain language description of the field set for documentation.
895        description = $description:literal,
896        // Length of the skeleton if this is the only field.
897        length_override = $length_override:ident,
898        // A sample string. A docs test will be generated!
899        sample = $sample:literal,
900        // The field symbol and field length.
901        field = $field:expr,
902        // The type in ZoneFieldSet for this field set
903        // Whether zone-essentials should be loaded.
904        $(zone_essentials = $zone_essentials_yes:ident,)?
905        // Whether locations names are needed.
906        $(zone_locations = $zone_locations_yes:ident,)?
907        // Whether exemplar city names are needed.
908        $(zone_exemplars = $zone_exemplars_yes:ident,)?
909        // Whether generic long names are needed.
910        $(zone_generic_long = $zone_generic_long_yes:ident,)?
911        // Whether generic short names are needed.
912        $(zone_generic_short = $zone_generic_short_yes:ident,)?
913        // Whether standard long names are needed.
914        $(zone_standard_long = $zone_standard_long_yes:ident,)?
915        // Whether specific long names are needed.
916        $(zone_specific_long = $zone_specific_long_yes:ident,)?
917        // Whether specific short names are needed.
918        $(zone_specific_short = $zone_specific_short_yes:ident,)?
919        // Whether metazone periods are needed
920        $(metazone_periods = $metazone_periods_yes:ident,)?
921        // Whether to require the TimeZone
922        $(input_tzid = $tzid_input_yes:ident,)?
923        // Whether to require the TimeZoneVariant
924        $(input_variant = $variant_input_yes:ident,)?
925        // Whether to require the Local Time
926        $(input_localtime = $localtime_input_yes:ident,)?
927    ) => {
928        #[doc = concat!("**“", $sample, "**” ⇒ ", $description)]
929        ///
930        /// # Examples
931        ///
932        /// ```
933        /// use icu::datetime::input::{Date, DateTime, Time, TimeZone, TimeZoneInfo, UtcOffset};
934        /// use icu::datetime::NoCalendarFormatter;
935        /// use icu::time::zone::TimeZoneVariant;
936        #[doc = concat!("use icu::datetime::fieldsets::zone::", stringify!($type), ";")]
937        /// use icu::locale::{locale, subtags::subtag};
938        /// use writeable::assert_writeable_eq;
939        ///
940        /// let fmt = NoCalendarFormatter::try_new(
941        ///     locale!("en").into(),
942        #[doc = concat!("    ", stringify!($type))]
943        /// )
944        /// .unwrap();
945        ///
946        /// // Time zone info for America/Chicago in the summer
947        /// let zone = TimeZone(subtag!("uschi"))
948        ///     .with_offset("-05".parse().ok())
949        ///     .at_date_time_iso(DateTime{ date: Date::try_new_iso(2022, 8, 29).unwrap(), time: Time::start_of_day() })
950        ///     .with_variant(TimeZoneVariant::Daylight);
951        ///
952        /// assert_writeable_eq!(
953        ///     fmt.format(&zone),
954        #[doc = concat!("    \"", $sample, "\"")]
955        /// );
956        /// ```
957        $(#[$attr])*
958        #[derive(Debug, Copy, Clone, PartialEq, Eq)]
959        #[allow(clippy::exhaustive_structs)] // singleton marker
960        pub struct $type;
961        impl UnstableSealed for $type {}
962        impl DateTimeNamesMarker for $type {
963            type YearNames = datetime_marker_helper!(@names/year,);
964            type MonthNames = datetime_marker_helper!(@names/month,);
965            type WeekdayNames = datetime_marker_helper!(@names/weekday,);
966            type DayPeriodNames = datetime_marker_helper!(@names/dayperiod,);
967            type ZoneEssentials = datetime_marker_helper!(@names/zone/essentials, $($zone_essentials_yes)?);
968            type ZoneLocations = datetime_marker_helper!(@names/zone/locations, $($zone_locations_yes)?);
969            type ZoneLocationsRoot = datetime_marker_helper!(@names/zone/locations_root, $($zone_locations_yes)?);
970            type ZoneExemplars = datetime_marker_helper!(@names/zone/exemplars, $($zone_exemplars_yes)?);
971            type ZoneExemplarsRoot = datetime_marker_helper!(@names/zone/exemplars_root, $($zone_exemplars_yes)?);
972            type ZoneGenericLong = datetime_marker_helper!(@names/zone/generic_long, $($zone_generic_long_yes)?);
973            type ZoneGenericShort = datetime_marker_helper!(@names/zone/generic_short, $($zone_generic_short_yes)?);
974            type ZoneStandardLong = datetime_marker_helper!(@names/zone/standard_long, $($zone_standard_long_yes)?);
975            type ZoneSpecificLong = datetime_marker_helper!(@names/zone/specific_long, $($zone_specific_long_yes)?);
976            type ZoneSpecificShort = datetime_marker_helper!(@names/zone/specific_short, $($zone_specific_short_yes)?);
977            type MetazoneLookup = datetime_marker_helper!(@names/zone/metazone_periods, $($metazone_periods_yes)?);
978        }
979        impl ZoneMarkers for $type {
980            type TimeZoneIdInput = datetime_marker_helper!(@input/timezone/id, $($tzid_input_yes)?);
981            type TimeZoneOffsetInput = datetime_marker_helper!(@input/timezone/offset, yes);
982            type TimeZoneVariantInput = datetime_marker_helper!(@input/timezone/variant, $($variant_input_yes)?);
983            type TimeZoneNameTimestampInput = datetime_marker_helper!(@input/timezone/local_time, $($localtime_input_yes)?);
984            type EssentialsV1 = datetime_marker_helper!(@data/zone/essentials, $($zone_essentials_yes)?);
985            type LocationsV1 = datetime_marker_helper!(@data/zone/locations, $($zone_locations_yes)?);
986            type LocationsRootV1 = datetime_marker_helper!(@data/zone/locations_root, $($zone_locations_yes)?);
987            type ExemplarCitiesV1 = datetime_marker_helper!(@data/zone/exemplars, $($zone_exemplars_yes)?);
988            type ExemplarCitiesRootV1 = datetime_marker_helper!(@data/zone/exemplars_root, $($zone_exemplars_yes)?);
989            type GenericLongV1 = datetime_marker_helper!(@data/zone/generic_long, $($zone_generic_long_yes)?);
990            type GenericShortV1 = datetime_marker_helper!(@data/zone/generic_short, $($zone_generic_short_yes)?);
991            type StandardLongV1 = datetime_marker_helper!(@data/zone/standard_long, $($zone_standard_long_yes)?);
992            type SpecificLongV1 = datetime_marker_helper!(@data/zone/specific_long, $($zone_specific_long_yes)?);
993            type SpecificShortV1 = datetime_marker_helper!(@data/zone/specific_short, $($zone_specific_short_yes)?);
994            type MetazonePeriodV1 = datetime_marker_helper!(@data/zone/metazone_periods, $($metazone_periods_yes)?);
995        }
996        impl DateTimeMarkers for $type {
997            type D = ();
998            type T = ();
999            type Z = Self;
1000            type GluePatternV1 = datetime_marker_helper!(@glue,);
1001        }
1002        impl_composite!($type, Zone, ZoneFieldSet);
1003        impl $type {
1004            pub(crate) fn to_field(self) -> (fields::TimeZone, fields::FieldLength) {
1005                $field
1006            }
1007        }
1008    };
1009}
1010
1011impl_date_marker!(
1012    /// This format may use ordinal formatting, such as "the 17th",
1013    /// in the future. See CLDR-18040.
1014    D,
1015    DT,
1016    description = "day of month (standalone)",
1017    sample_length = short,
1018    sample = "17",
1019    sample_time = "17, 3:47:50 PM",
1020    input_day_of_month = yes,
1021    input_any_calendar_kind = yes,
1022    option_alignment = yes,
1023);
1024
1025impl_date_marker!(
1026    E,
1027    ET,
1028    description = "weekday (standalone)",
1029    sample_length = long,
1030    sample = "Friday",
1031    sample_time = "Friday 3:47:50 PM",
1032    weekdays = yes,
1033    input_day_of_week = yes,
1034);
1035
1036impl_date_marker!(
1037    /// This format may use ordinal formatting, such as "Friday the 17th",
1038    /// in the future. See CLDR-18040.
1039    DE,
1040    DET,
1041    description = "day of month and weekday",
1042    sample_length = long,
1043    sample = "17 Friday",
1044    sample_time = "17 Friday, 3:47:50 PM",
1045    weekdays = yes,
1046    input_day_of_month = yes,
1047    input_day_of_week = yes,
1048    option_alignment = yes,
1049);
1050
1051impl_date_marker!(
1052    MD,
1053    MDT,
1054    description = "month and day",
1055    sample_length = medium,
1056    sample = "May 17",
1057    sample_time = "May 17, 3:47:50 PM",
1058    months = yes,
1059    input_month = yes,
1060    input_day_of_month = yes,
1061    input_any_calendar_kind = yes,
1062    option_alignment = yes,
1063);
1064
1065impl_date_marker!(
1066    /// See CLDR-18040 for progress on improving this format.
1067    MDE,
1068    MDET,
1069    description = "month, day, and weekday",
1070    sample_length = medium,
1071    sample = "Fri, May 17",
1072    sample_time = "Fri, May 17, 3:47:50 PM",
1073    months = yes,
1074    weekdays = yes,
1075    input_month = yes,
1076    input_day_of_month = yes,
1077    input_day_of_week = yes,
1078    input_any_calendar_kind = yes,
1079    option_alignment = yes,
1080);
1081
1082impl_date_marker!(
1083    YMD,
1084    YMDT,
1085    description = "year, month, and day",
1086    sample_length = short,
1087    sample = "5/17/24",
1088    sample_time = "5/17/24, 3:47:50 PM",
1089    years = yes,
1090    months = yes,
1091    input_year = yes,
1092    input_month = yes,
1093    input_day_of_month = yes,
1094    input_any_calendar_kind = yes,
1095    option_alignment = yes,
1096);
1097
1098impl_date_marker!(
1099    YMDE,
1100    YMDET,
1101    description = "year, month, day, and weekday",
1102    sample_length = short,
1103    sample = "Fri, 5/17/24",
1104    sample_time = "Fri, 5/17/24, 3:47:50 PM",
1105    years = yes,
1106    months = yes,
1107    weekdays = yes,
1108    input_year = yes,
1109    input_month = yes,
1110    input_day_of_month = yes,
1111    input_day_of_week = yes,
1112    input_any_calendar_kind = yes,
1113    option_alignment = yes,
1114);
1115
1116impl_calendar_period_marker!(
1117    Y,
1118    description = "year (standalone)",
1119    sample_length = medium,
1120    sample = "2024",
1121    years = yes,
1122    input_year = yes,
1123    input_any_calendar_kind = yes,
1124    option_alignment = yes,
1125);
1126
1127impl_calendar_period_marker!(
1128    M,
1129    description = "month (standalone)",
1130    sample_length = long,
1131    sample = "May",
1132    months = yes,
1133    input_month = yes,
1134    input_any_calendar_kind = yes,
1135    option_alignment = yes,
1136);
1137
1138impl_calendar_period_marker!(
1139    YM,
1140    description = "year and month",
1141    sample_length = medium,
1142    sample = "May 2024",
1143    years = yes,
1144    months = yes,
1145    input_year = yes,
1146    input_month = yes,
1147    input_any_calendar_kind = yes,
1148    option_alignment = yes,
1149);
1150
1151impl_time_marker!(
1152    /// Hours can be switched between 12-hour and 24-hour time via the `u-hc` locale keyword
1153    /// or [`DateTimeFormatterPreferences`].
1154    ///
1155    /// ```
1156    /// use icu::datetime::input::Time;
1157    /// use icu::datetime::fieldsets::T;
1158    /// use icu::datetime::NoCalendarFormatter;
1159    /// use icu::locale::locale;
1160    /// use writeable::assert_writeable_eq;
1161    ///
1162    /// // By default, en-US uses 12-hour time and fr-FR uses 24-hour time,
1163    /// // but we can set overrides.
1164    ///
1165    /// let formatter = NoCalendarFormatter::try_new(
1166    ///     locale!("en-US-u-hc-h12").into(),
1167    ///     T::hm(),
1168    /// )
1169    /// .unwrap();
1170    /// assert_writeable_eq!(
1171    ///     formatter.format(&Time::try_new(16, 12, 20, 0).unwrap()),
1172    ///     "4:12 PM"
1173    /// );
1174    ///
1175    /// let formatter = NoCalendarFormatter::try_new(
1176    ///     locale!("en-US-u-hc-h23").into(),
1177    ///     T::hm(),
1178    /// )
1179    /// .unwrap();
1180    /// assert_writeable_eq!(
1181    ///     formatter.format(&Time::try_new(16, 12, 20, 0).unwrap()),
1182    ///     "16:12"
1183    /// );
1184    ///
1185    /// let formatter = NoCalendarFormatter::try_new(
1186    ///     locale!("fr-FR-u-hc-h12").into(),
1187    ///     T::hm(),
1188    /// )
1189    /// .unwrap();
1190    /// assert_writeable_eq!(
1191    ///     formatter.format(&Time::try_new(16, 12, 20, 0).unwrap()),
1192    ///     "4:12 PM"
1193    /// );
1194    ///
1195    /// let formatter = NoCalendarFormatter::try_new(
1196    ///     locale!("fr-FR-u-hc-h23").into(),
1197    ///     T::hm(),
1198    /// )
1199    /// .unwrap();
1200    /// assert_writeable_eq!(
1201    ///     formatter.format(&Time::try_new(16, 12, 20, 0).unwrap()),
1202    ///     "16:12"
1203    /// );
1204    /// ```
1205    ///
1206    /// Hour cycles `h11` and `h24` are supported, too:
1207    ///
1208    /// ```
1209    /// use icu::datetime::input::Time;
1210    /// use icu::datetime::fieldsets::T;
1211    /// use icu::datetime::NoCalendarFormatter;
1212    /// use icu::locale::locale;
1213    /// use writeable::assert_writeable_eq;
1214    ///
1215    /// let formatter = NoCalendarFormatter::try_new(
1216    ///     locale!("und-u-hc-h11").into(),
1217    ///     T::hm(),
1218    /// )
1219    /// .unwrap();
1220    ///
1221    /// assert_writeable_eq!(
1222    ///     formatter.format(&Time::try_new(0, 0, 0, 0).unwrap()),
1223    ///     "0:00 AM"
1224    /// );
1225    ///
1226    /// let formatter = NoCalendarFormatter::try_new(
1227    ///     locale!("und-u-hc-h23").into(),
1228    ///     T::hm(),
1229    /// )
1230    /// .unwrap();
1231    ///
1232    /// assert_writeable_eq!(
1233    ///     formatter.format(&Time::try_new(0, 0, 0, 0).unwrap()),
1234    ///     "00:00"
1235    /// );
1236    /// ```
1237    ///
1238    /// Conveniently construct a time formatter with subseconds:
1239    ///
1240    /// ```
1241    /// use icu::datetime::input::Time;
1242    /// use icu::datetime::fieldsets::T;
1243    /// use icu::datetime::options::SubsecondDigits;
1244    /// use icu::datetime::NoCalendarFormatter;
1245    /// use icu::locale::locale;
1246    /// use writeable::assert_writeable_eq;
1247    ///
1248    /// let formatter = NoCalendarFormatter::try_new(
1249    ///     locale!("en").into(),
1250    ///     T::hmss(SubsecondDigits::S4),
1251    /// )
1252    /// .unwrap();
1253    ///
1254    /// assert_writeable_eq!(
1255    ///     formatter.format(&Time::try_new(18, 24, 36, 987654321).unwrap()),
1256    ///     "6:24:36.9876 PM"
1257    /// );
1258    /// ```
1259    ///
1260    /// [`DateTimeFormatterPreferences`]: crate::DateTimeFormatterPreferences
1261    T,
1262    description = "time (locale-dependent hour cycle)",
1263    sample_length = medium,
1264    sample = "3:47:50 PM",
1265    dayperiods = yes,
1266    input_hour = yes,
1267    input_minute = yes,
1268    input_second = yes,
1269    input_subsecond = yes,
1270);
1271
1272/// Time zone field sets
1273pub mod zone {
1274    use super::*;
1275    impl_zone_marker!(
1276        /// When a display name is unavailable, falls back to the localized offset format for short lengths, and
1277        /// to the location format for long lengths:
1278        ///
1279        /// ```
1280        /// use icu::datetime::input::{Date, DateTime, Time, TimeZone, TimeZoneInfo, UtcOffset};
1281        /// use icu::calendar::Gregorian;
1282        /// use icu::datetime::FixedCalendarDateTimeFormatter;
1283        /// use icu::datetime::fieldsets::zone::{SpecificLong, SpecificShort};
1284        /// use icu::locale::{locale, subtags::subtag};
1285        /// use icu::time::zone::TimeZoneVariant;
1286        /// use writeable::assert_writeable_eq;
1287        ///
1288        /// // Time zone info for Europe/Istanbul in the winter
1289        /// let zone = TimeZone(subtag!("trist"))
1290        ///     .with_offset("+02".parse().ok())
1291        ///     .at_date_time_iso(DateTime{ date: Date::try_new_iso(2022, 1, 29).unwrap(), time: Time::start_of_day() })
1292        ///     .with_variant(TimeZoneVariant::Standard);
1293        ///
1294        /// let fmt = FixedCalendarDateTimeFormatter::<Gregorian, _>::try_new(
1295        ///     locale!("en").into(),
1296        ///     SpecificShort,
1297        /// )
1298        /// .unwrap();
1299        ///
1300        /// assert_writeable_eq!(
1301        ///     fmt.format(&zone),
1302        ///     "GMT+2"
1303        /// );
1304        ///
1305        /// let fmt = FixedCalendarDateTimeFormatter::<Gregorian, _>::try_new(
1306        ///     locale!("en").into(),
1307        ///     SpecificLong,
1308        /// )
1309        /// .unwrap();
1310        ///
1311        /// assert_writeable_eq!(
1312        ///     fmt.format(&zone),
1313        ///     "Türkiye Standard Time"
1314        /// );
1315        /// ```
1316        ///
1317        /// This style requires a [`TimeZoneVariant`], so
1318        /// only a full time zone info can be formatted with this style.
1319        /// For example, [`TimeZoneInfo<AtTime>`] cannot be formatted.
1320        ///
1321        /// ```compile_fail,E0271
1322        /// use icu::datetime::input::{Date, DateTime, Iso};
1323        /// use icu::datetime::FixedCalendarDateTimeFormatter;
1324        /// use icu::datetime::fieldsets::zone::SpecificLong;
1325        /// use icu::locale::{locale, subtags::subtag};
1326        /// use icu::datetime::input::{DateTime, Time, TimeZone, UtcOffset};
1327        /// use icu::time::zone::TimeZoneVariant;
1328        /// use writeable::assert_writeable_eq;
1329        ///
1330        /// let datetime = DateTime { date: Date::try_new_gregorian(2024, 10, 18).unwrap(), time: Time::start_of_day() };
1331        /// let time_zone_basic = TimeZone(subtag!("uschi")).with_offset("-06".parse().ok());
1332        /// let time_zone_at_time = time_zone_basic.at_date_time_iso(DateTime{ date: datetime.date.to_iso(), time: datetime.time });
1333        ///
1334        /// let formatter = FixedCalendarDateTimeFormatter::try_new(
1335        ///     locale!("en-US").into(),
1336        ///     SpecificLong,
1337        /// )
1338        /// .unwrap();
1339        ///
1340        /// // error[E0271]: type mismatch resolving `<AtTime as TimeZoneModel>::TimeZoneVariant == TimeZoneVariant`
1341        /// formatter.format(&time_zone_at_time);
1342        /// ```
1343        SpecificLong,
1344        description = "time zone in specific non-location format, long length",
1345        length_override = Long,
1346        sample = "Central Daylight Time",
1347        field = (fields::TimeZone::SpecificNonLocation, fields::FieldLength::Four),
1348        zone_essentials = yes,
1349        zone_locations = yes,
1350        zone_standard_long = yes,
1351        zone_specific_long = yes,
1352        metazone_periods = yes,
1353        input_tzid = yes,
1354        input_variant = yes,
1355        input_localtime = yes,
1356    );
1357
1358    impl_zone_marker!(
1359        /// This style requires a [`TimeZoneVariant`], so
1360        /// only a full time zone info can be formatted with this style.
1361        /// For example, [`TimeZoneInfo<AtTime>`] cannot be formatted.
1362        ///
1363        /// ```compile_fail,E0271
1364        /// use icu::datetime::input::{Date, DateTime, Iso};
1365        /// use icu::datetime::FixedCalendarDateTimeFormatter;
1366        /// use icu::datetime::fieldsets::{T, zone::SpecificShort};
1367        /// use icu::locale::{locale, subtags::subtag};
1368        /// use icu::datetime::input::{DateTime, Time, TimeZone, UtcOffset};
1369        /// use icu::time::zone::TimeZoneVariant;
1370        /// use writeable::assert_writeable_eq;
1371        ///
1372        /// let datetime = DateTime { Date::try_new_gregorian(2024, 10, 18).unwrap(), time: Time::start_of_day() };
1373        /// let time_zone_basic = TimeZone(subtag!("uschi")).with_offset("-06".parse().ok());
1374        /// let time_zone_at_time = time_zone_basic.at_date_time_iso(DateTime{ date: datetime.date.to_iso(), time: datetime.time });
1375        ///
1376        /// let formatter = FixedCalendarDateTimeFormatter::try_new(
1377        ///     locale!("en-US").into(),
1378        ///     T::medium().with_zone(SpecificShort),
1379        /// )
1380        /// .unwrap();
1381        ///
1382        /// // error[E0271]: type mismatch resolving `<AtTime as TimeZoneModel>::TimeZoneVariant == TimeZoneVariant`
1383        /// // note: required by a bound in `FixedCalendarDateTimeFormatter::<C, FSet>::format`
1384        /// formatter.format(&time_zone_at_time);
1385        /// ```
1386        SpecificShort,
1387        description = "time zone in specific non-location format, short length",
1388        length_override = Short,
1389        sample = "CDT",
1390        field = (fields::TimeZone::SpecificNonLocation, fields::FieldLength::One),
1391        zone_essentials = yes,
1392        zone_specific_short = yes,
1393        metazone_periods = yes,
1394        input_tzid = yes,
1395        input_variant = yes,
1396        input_localtime = yes,
1397    );
1398
1399    impl_zone_marker!(
1400        /// All shapes of time zones can be formatted with this style.
1401        ///
1402        /// ```
1403        /// use icu::datetime::input::Date;
1404        /// use icu::datetime::NoCalendarFormatter;
1405        /// use icu::datetime::fieldsets::zone::LocalizedOffsetLong;
1406        /// use icu::datetime::input::{DateTime, Time, TimeZone, UtcOffset};
1407        /// use icu::time::zone::TimeZoneVariant;
1408        /// use icu::locale::{locale, subtags::subtag};
1409        /// use writeable::assert_writeable_eq;
1410        ///
1411        /// let utc_offset = "-06".parse().unwrap();
1412        /// let time_zone_basic = TimeZone(subtag!("uschi")).with_offset(Some(utc_offset));
1413        ///
1414        /// let date = Date::try_new_iso(2024, 10, 18).unwrap();
1415        /// let time = Time::start_of_day();
1416        /// let time_zone_at_time = time_zone_basic.at_date_time_iso(DateTime{ date, time });
1417        ///
1418        /// let time_zone_full = time_zone_at_time.with_variant(TimeZoneVariant::Standard);
1419        ///
1420        /// let formatter = NoCalendarFormatter::try_new(
1421        ///     locale!("en-US").into(),
1422        ///     LocalizedOffsetLong,
1423        /// )
1424        /// .unwrap();
1425        ///
1426        /// assert_writeable_eq!(
1427        ///     formatter.format(&utc_offset),
1428        ///     "GMT-06:00"
1429        /// );
1430        ///
1431        /// assert_writeable_eq!(
1432        ///     formatter.format(&time_zone_basic),
1433        ///     "GMT-06:00"
1434        /// );
1435        ///
1436        /// assert_writeable_eq!(
1437        ///     formatter.format(&time_zone_at_time),
1438        ///     "GMT-06:00"
1439        /// );
1440        ///
1441        /// assert_writeable_eq!(
1442        ///     formatter.format(&time_zone_full),
1443        ///     "GMT-06:00"
1444        /// );
1445        /// ```
1446        LocalizedOffsetLong,
1447        description = "UTC offset, long length",
1448        length_override = Long,
1449        sample = "GMT-05:00",
1450        field = (fields::TimeZone::LocalizedOffset, fields::FieldLength::Four),
1451        zone_essentials = yes,
1452    );
1453
1454    impl_zone_marker!(
1455        LocalizedOffsetShort,
1456        description = "UTC offset, short length",
1457        length_override = Short,
1458        sample = "GMT-5",
1459        field = (fields::TimeZone::LocalizedOffset, fields::FieldLength::One),
1460        zone_essentials = yes,
1461    );
1462
1463    impl_zone_marker!(
1464        /// When a display name is unavailable, falls back to the location format:
1465        ///
1466        /// ```
1467        /// use icu::datetime::input::{Date, DateTime, Time, TimeZone};
1468        /// use icu::calendar::Gregorian;
1469        /// use icu::datetime::FixedCalendarDateTimeFormatter;
1470        /// use icu::datetime::fieldsets::zone::GenericShort;
1471        /// use icu::locale::{locale, subtags::subtag};
1472        /// use writeable::assert_writeable_eq;
1473        ///
1474        /// // Time zone info for Europe/Istanbul
1475        /// let zone = TimeZone(subtag!("trist"))
1476        ///     .without_offset()
1477        ///     .at_date_time_iso(DateTime{ date: Date::try_new_iso(2022, 1, 29).unwrap(), time: Time::start_of_day() });
1478        ///
1479        /// let fmt = FixedCalendarDateTimeFormatter::<Gregorian, _>::try_new(
1480        ///     locale!("en").into(),
1481        ///     GenericShort,
1482        /// )
1483        /// .unwrap();
1484        ///
1485        /// assert_writeable_eq!(
1486        ///     fmt.format(&zone),
1487        ///     "Türkiye Time"
1488        /// );
1489        /// ```
1490        ///
1491        /// Can also fall back to the UTC offset:
1492        ///
1493        /// ```
1494        /// use icu::datetime::input::{Date, DateTime, Time};
1495        /// use icu::datetime::NoCalendarFormatter;
1496        /// use icu::datetime::fieldsets::zone::GenericShort;
1497        /// use icu::locale::locale;
1498        /// use icu::time::zone::IanaParser;
1499        /// use tinystr::tinystr;
1500        /// use writeable::assert_writeable_eq;
1501        ///
1502        /// // Set up the formatter
1503        /// let mut tzf = NoCalendarFormatter::try_new(
1504        ///     locale!("en").into(),
1505        ///     GenericShort,
1506        /// )
1507        /// .unwrap();
1508        ///
1509        /// // "uschi" - has symbol data for short generic non-location
1510        /// let time_zone = IanaParser::new()
1511        ///     .parse("America/Chicago")
1512        ///     .with_offset("-05".parse().ok())
1513        ///     .at_date_time_iso(DateTime{ date: Date::try_new_iso(2022, 8, 29).unwrap(), time: Time::start_of_day() });
1514        /// assert_writeable_eq!(
1515        ///     tzf.format(&time_zone),
1516        ///     "CT"
1517        /// );
1518        ///
1519        /// // "ushnl" - has time zone override symbol data for short generic non-location
1520        /// let time_zone = IanaParser::new()
1521        ///     .parse("Pacific/Honolulu")
1522        ///     .with_offset("-10".parse().ok())
1523        ///     .at_date_time_iso(DateTime{ date: Date::try_new_iso(2022, 8, 29).unwrap(), time: Time::start_of_day() });
1524        /// assert_writeable_eq!(
1525        ///     tzf.format(&time_zone),
1526        ///     "HST"
1527        /// );
1528        ///
1529        /// // Mis-spelling of "America/Chicago" results in a fallback to offset format
1530        /// let time_zone = IanaParser::new()
1531        ///     .parse("America/Chigagou")
1532        ///     .with_offset("-05".parse().ok())
1533        ///     .at_date_time_iso(DateTime{ date: Date::try_new_iso(2022, 8, 29).unwrap(), time: Time::start_of_day() });
1534        /// assert_writeable_eq!(
1535        ///     tzf.format(&time_zone),
1536        ///     "GMT-5"
1537        /// );
1538        /// ```
1539        ///
1540        /// Since non-location names might change over time,
1541        /// this time zone style requires a reference time.
1542        ///
1543        /// ```compile_fail,E0271
1544        /// use icu::datetime::NoCalendarFormatter;
1545        /// use icu::datetime::fieldsets::zone::GenericLong;
1546        /// use icu::datetime::input::TimeZone;
1547        /// use icu::locale::{locale, subtags::subtag};
1548        /// use writeable::assert_writeable_eq;
1549        ///
1550        /// let time_zone_basic = TimeZone(subtag!("uschi")).without_offset();
1551        ///
1552        /// let formatter = NoCalendarFormatter::try_new(
1553        ///     locale!("en-US").into(),
1554        ///     GenericLong,
1555        /// )
1556        /// .unwrap();
1557        ///
1558        /// // error[E0271]: type mismatch resolving `<Base as TimeZoneModel>::LocalTime == (Date<Iso>, Time)`
1559        /// // note: required by a bound in `NoCalendarFormatter::<C, FSet>::format`
1560        /// formatter.format(&time_zone_basic);
1561        /// ```
1562        GenericLong,
1563        description = "time zone in generic non-location format, long length",
1564        length_override = Long,
1565        sample = "Central Time",
1566        field = (fields::TimeZone::GenericNonLocation, fields::FieldLength::Four),
1567        zone_essentials = yes,
1568        zone_locations = yes,
1569        zone_generic_long = yes,
1570        zone_standard_long = yes,
1571        metazone_periods = yes,
1572        input_tzid = yes,
1573        input_localtime = yes,
1574    );
1575
1576    impl_zone_marker!(
1577        /// Note: short time zones names are usually only available for time zones in the country
1578        /// associated with a locale (so "PT" is in `en`, but not in `en-GB`). Most time zones will
1579        /// fall back to the significantly longer location format (e.g. "Los Angeles Time" in `en-GB`).
1580        ///
1581        /// Since non-location names might change over time,
1582        /// this time zone style requires a reference time.
1583        ///
1584        /// ```compile_fail,E0271
1585        /// use icu::datetime::FixedCalendarDateTimeFormatter;
1586        /// use icu::datetime::fieldsets::zone::GenericShort;
1587        /// use icu::datetime::input::TimeZone;
1588        /// use icu::locale::{locale, subtags::subtag};
1589        /// use writeable::assert_writeable_eq;
1590        ///
1591        /// let time_zone_basic = TimeZone(subtag!("uschi")).with_offset("-06".parse().ok());
1592        ///
1593        /// let formatter = FixedCalendarDateTimeFormatter::try_new(
1594        ///     locale!("en-US").into(),
1595        ///     GenericShort,
1596        /// )
1597        /// .unwrap();
1598        ///
1599        /// // error[E0271]: type mismatch resolving `<Base as TimeZoneModel>::LocalTime == (Date<Iso>, Time)`
1600        /// // note: required by a bound in `FixedCalendarDateTimeFormatter::<C, FSet>::format`
1601        /// formatter.format(&time_zone_basic);
1602        /// ```
1603        GenericShort,
1604        description = "time zone in generic non-location format, short length",
1605        length_override = Short,
1606        sample = "CT",
1607        field = (fields::TimeZone::GenericNonLocation, fields::FieldLength::One),
1608        zone_essentials = yes,
1609        zone_locations = yes,
1610        zone_generic_short = yes,
1611        metazone_periods = yes,
1612        input_tzid = yes,
1613        input_localtime = yes,
1614    );
1615
1616    impl_zone_marker!(
1617        /// A time zone ID is required to format with this style.
1618        /// For example, a raw [`UtcOffset`] cannot be used here.
1619        ///
1620        /// ```compile_fail,E0277
1621        /// use icu::datetime::input::{DateTime, Iso};
1622        /// use icu::datetime::FixedCalendarDateTimeFormatter;
1623        /// use icu::datetime::fieldsets::zone::Location;
1624        /// use icu::datetime::input::UtcOffset;
1625        /// use tinystr::tinystr;
1626        /// use icu::locale::locale;
1627        /// use writeable::assert_writeable_eq;
1628        ///
1629        /// let utc_offset = UtcOffset::try_from_str("-06").unwrap();
1630        ///
1631        /// let formatter = FixedCalendarDateTimeFormatter::try_new(
1632        ///     locale!("en-US").into(),
1633        ///     Location,
1634        /// )
1635        /// .unwrap();
1636        ///
1637        /// // error[E0277]: the trait bound `UtcOffset: AllInputMarkers<Location>` is not satisfied
1638        /// // note: required by a bound in `FixedCalendarDateTimeFormatter::<C, FSet>::format`
1639        /// formatter.format(&utc_offset);
1640        /// ```
1641        Location,
1642        description = "time zone in location format",
1643        length_override = Long,
1644        sample = "Chicago Time",
1645        field = (fields::TimeZone::Location, fields::FieldLength::Four),
1646        zone_essentials = yes,
1647        zone_locations = yes,
1648        input_tzid = yes,
1649    );
1650
1651    impl_zone_marker!(
1652        /// A time zone ID is required to format with this style.
1653        /// For example, a raw [`UtcOffset`] cannot be used here.
1654        ///
1655        /// ```compile_fail,E0277
1656        /// use icu::datetime::input::{DateTime, Iso};
1657        /// use icu::datetime::FixedCalendarDateTimeFormatter;
1658        /// use icu::datetime::fieldsets::zone::ExemplarCity;
1659        /// use icu::datetime::input::UtcOffset;
1660        /// use tinystr::tinystr;
1661        /// use icu::locale::locale;
1662        /// use writeable::assert_writeable_eq;
1663        ///
1664        /// let utc_offset = UtcOffset::try_from_str("-06").unwrap();
1665        ///
1666        /// let formatter = FixedCalendarDateTimeFormatter::try_new(
1667        ///     locale!("en-US").into(),
1668        ///     ExemplarCity,
1669        /// )
1670        /// .unwrap();
1671        ///
1672        /// // error[E0277]: the trait bound `UtcOffset: AllInputMarkers<ExemplarCity>` is not satisfied
1673        /// // note: required by a bound in `FixedCalendarDateTimeFormatter::<C, FSet>::format`
1674        /// formatter.format(&utc_offset);
1675        /// ```
1676        ExemplarCity,
1677        description = "time zone in exemplar city format",
1678        length_override = Long,
1679        sample = "Chicago",
1680        field = (fields::TimeZone::Location, fields::FieldLength::Three),
1681        zone_locations = yes,
1682        zone_exemplars = yes,
1683        input_tzid = yes,
1684    );
1685}
1686
1687impl_zone_combo_helpers!(DateFieldSet, DateZone, DateFieldSet);
1688
1689impl_zone_combo_helpers!(TimeFieldSet, TimeZone, TimeFieldSet);
1690
1691impl_zone_combo_helpers!(DateAndTimeFieldSet, DateTimeZone, DateAndTimeFieldSet);