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);