Skip to content

Commit 0e328c5

Browse files
authored
Bring time zone length behavior up to spec (#5563)
1 parent d704ef7 commit 0e328c5

File tree

8 files changed

+219
-197
lines changed

8 files changed

+219
-197
lines changed

components/datetime/src/neo_marker.rs

Lines changed: 163 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -254,13 +254,14 @@
254254
//! ## Time Zone Formatting
255255
//!
256256
//! Here, we configure a [`NeoFormatter`] to format with generic non-location short,
257-
//! which falls back to the offset when unavailable (see [`NeoTimeZoneGenericShortMarker`]).
257+
//! which falls back to the offset when unavailable (see [`NeoTimeZoneGenericMarker`]).
258258
//!
259259
//! ```
260260
//! use icu::calendar::DateTime;
261261
//! use icu::timezone::{CustomTimeZone, MetazoneCalculator, TimeZoneIdMapper, TimeZoneBcp47Id};
262262
//! use icu::datetime::neo::TypedNeoFormatter;
263-
//! use icu::datetime::neo_marker::NeoTimeZoneGenericShortMarker;
263+
//! use icu::datetime::neo_marker::NeoTimeZoneGenericMarker;
264+
//! use icu::datetime::neo_skeleton::NeoSkeletonLength;
264265
//! use icu::datetime::NeverCalendar;
265266
//! use icu::locale::locale;
266267
//! use tinystr::tinystr;
@@ -280,10 +281,9 @@
280281
//! .unwrap();
281282
//!
282283
//! // Set up the formatter
283-
//! let mut tzf = TypedNeoFormatter::<NeverCalendar, NeoTimeZoneGenericShortMarker>::try_new(
284+
//! let mut tzf = TypedNeoFormatter::<NeverCalendar, NeoTimeZoneGenericMarker>::try_new(
284285
//! &locale!("en").into(),
285-
//! // Length does not matter here: it is specified in the type parameter
286-
//! Default::default(),
286+
//! NeoSkeletonLength::Short.into(),
287287
//! )
288288
//! .unwrap();
289289
//!
@@ -1782,8 +1782,8 @@ macro_rules! impl_date_marker {
17821782
/// ```
17831783
$type,
17841784
sample_length: $sample_length,
1785-
$(alignment: $option_alignment_yes)?,
1786-
$(era_display: $year_yes)?,
1785+
$(alignment: $option_alignment_yes,)?
1786+
$(era_display: $year_yes,)?
17871787
);
17881788
impl private::Sealed for $type {}
17891789
impl DateTimeNamesMarker for $type {
@@ -2030,8 +2030,7 @@ macro_rules! impl_zone_marker {
20302030
// A plain language description of the field set for documentation.
20312031
description = $description:literal,
20322032
// Length of the sample string below.
2033-
// Omit if this field set does not accept a length.
2034-
$(sample_length = $sample_length:ident,)?
2033+
sample_length = $sample_length:ident,
20352034
// A sample string. A docs test will be generated!
20362035
sample = $sample:literal,
20372036
// Whether zone-essentials should be loaded.
@@ -2064,7 +2063,7 @@ macro_rules! impl_zone_marker {
20642063
///
20652064
#[doc = concat!("let fmt = NeoFormatter::<", stringify!($type), ">::try_new(")]
20662065
/// &locale!("en").into(),
2067-
#[doc = concat!(" ", length_option_helper!($($sample_length)?), ",")]
2066+
#[doc = concat!(" ", length_option_helper!($sample_length), ",")]
20682067
/// )
20692068
/// .unwrap();
20702069
///
@@ -2097,7 +2096,7 @@ macro_rules! impl_zone_marker {
20972096
///
20982097
#[doc = concat!("let fmt = TypedNeoFormatter::<Gregorian, ", stringify!($type), ">::try_new(")]
20992098
/// &locale!("en").into(),
2100-
#[doc = concat!(" ", length_option_helper!($($sample_length)?), ",")]
2099+
#[doc = concat!(" ", length_option_helper!($sample_length), ",")]
21012100
/// )
21022101
/// .unwrap();
21032102
///
@@ -2150,7 +2149,7 @@ macro_rules! impl_zone_marker {
21502149
type D = NeoNeverMarker;
21512150
type T = NeoNeverMarker;
21522151
type Z = Self;
2153-
type LengthOption = datetime_marker_helper!(@option/length, $($sample_length)?);
2152+
type LengthOption = datetime_marker_helper!(@option/length, yes);
21542153
type AlignmentOption = datetime_marker_helper!(@option/alignment,);
21552154
type EraDisplayOption = datetime_marker_helper!(@option/eradisplay,);
21562155
type FractionalSecondDigitsOption = datetime_marker_helper!(@option/fractionalsecondigits,);
@@ -2327,6 +2326,19 @@ impl_day_marker!(
23272326
option_alignment = yes,
23282327
);
23292328

2329+
impl_day_marker!(
2330+
NeoMonthDayMarker,
2331+
NeoDayComponents::MonthDay,
2332+
description = "month and day",
2333+
sample_length = Medium,
2334+
sample = "May 17",
2335+
months = yes,
2336+
input_month = yes,
2337+
input_day_of_month = yes,
2338+
input_any_calendar_kind = yes,
2339+
option_alignment = yes,
2340+
);
2341+
23302342
impl_day_marker!(
23312343
NeoAutoDateMarker,
23322344
NeoDayComponents::Auto,
@@ -2416,16 +2428,6 @@ impl_date_marker!(
24162428
option_alignment = yes,
24172429
);
24182430

2419-
impl_zone_marker!(
2420-
NeoTimeZoneSpecificMarker,
2421-
NeoTimeZoneSkeleton::specific(),
2422-
description = "specific time zone with inherited length, or raw offset if unavailable",
2423-
sample_length = Medium,
2424-
sample = "CDT",
2425-
zone_essentials = yes,
2426-
zone_specific_short = yes,
2427-
);
2428-
24292431
impl_zone_marker!(
24302432
/// When a display name is unavailable, falls back to the offset format:
24312433
///
@@ -2434,15 +2436,15 @@ impl_zone_marker!(
24342436
/// use icu::timezone::{CustomTimeZone, CustomZonedDateTime};
24352437
/// use icu::calendar::Gregorian;
24362438
/// use icu::datetime::neo::TypedNeoFormatter;
2437-
/// use icu::datetime::neo_marker::NeoTimeZoneSpecificShortMarker;
2439+
/// use icu::datetime::neo_marker::NeoTimeZoneSpecificMarker;
24382440
/// use icu::datetime::neo_skeleton::NeoSkeletonLength;
24392441
/// use icu::locale::locale;
24402442
/// use tinystr::tinystr;
24412443
/// use writeable::assert_try_writeable_eq;
24422444
///
2443-
/// let fmt = TypedNeoFormatter::<Gregorian, NeoTimeZoneSpecificShortMarker>::try_new(
2445+
/// let fmt = TypedNeoFormatter::<Gregorian, NeoTimeZoneSpecificMarker>::try_new(
24442446
/// &locale!("en").into(),
2445-
/// Default::default(),
2447+
/// NeoSkeletonLength::Short.into(),
24462448
/// )
24472449
/// .unwrap();
24482450
///
@@ -2459,59 +2461,88 @@ impl_zone_marker!(
24592461
/// "GMT-03:00"
24602462
/// );
24612463
/// ```
2462-
NeoTimeZoneSpecificShortMarker,
2463-
NeoTimeZoneSkeleton::specific_short(),
2464-
description = "specific time zone with a shorter length, or raw offset if unavailable",
2465-
sample = "CDT",
2464+
NeoTimeZoneSpecificMarker,
2465+
NeoTimeZoneSkeleton::specific(),
2466+
description = "specific time zone, or raw offset if unavailable",
2467+
sample_length = Long,
2468+
sample = "Central Daylight Time",
24662469
zone_essentials = yes,
2470+
zone_specific_long = yes,
24672471
zone_specific_short = yes,
24682472
);
24692473

24702474
impl_zone_marker!(
2471-
NeoTimeZoneSpecificLongMarker,
2472-
NeoTimeZoneSkeleton::specific_long(),
2473-
description = "specific time zone with a longer length, or raw offset if unavailable",
2474-
sample = "Central Daylight Time",
2475+
/// This marker only loads data for the short length. Useful when combined with other fields:
2476+
///
2477+
/// ```
2478+
/// use icu::calendar::{Date, Time};
2479+
/// use icu::timezone::{CustomTimeZone, CustomZonedDateTime};
2480+
/// use icu::calendar::Gregorian;
2481+
/// use icu::datetime::neo::NeoFormatter;
2482+
/// use icu::datetime::neo_marker::NeoMonthDayMarker;
2483+
/// use icu::datetime::neo_marker::NeoHourMinuteMarker;
2484+
/// use icu::datetime::neo_marker::NeoTimeZoneSpecificShortMarker;
2485+
/// use icu::datetime::neo_marker::DateTimeCombo;
2486+
/// use icu::datetime::neo_skeleton::NeoSkeletonLength;
2487+
/// use icu::locale::locale;
2488+
/// use tinystr::tinystr;
2489+
/// use writeable::assert_try_writeable_eq;
2490+
///
2491+
/// type MyDateTimeZoneSet = DateTimeCombo<
2492+
/// NeoMonthDayMarker,
2493+
/// NeoHourMinuteMarker,
2494+
/// NeoTimeZoneSpecificShortMarker,
2495+
/// >;
2496+
///
2497+
/// let fmt = NeoFormatter::<MyDateTimeZoneSet>::try_new(
2498+
/// &locale!("en-US").into(),
2499+
/// NeoSkeletonLength::Long.into(),
2500+
/// )
2501+
/// .unwrap();
2502+
///
2503+
/// let dtz = CustomZonedDateTime::try_from_str("2024-09-17T15:47:50-05:00[America/Chicago]").unwrap();
2504+
///
2505+
/// assert_try_writeable_eq!(
2506+
/// fmt.convert_and_format(&dtz),
2507+
/// "September 17, 3:47 PM CDT"
2508+
/// );
2509+
/// ```
2510+
///
2511+
/// Don't use long length if it is the only field:
2512+
///
2513+
/// ```
2514+
/// use icu::calendar::Gregorian;
2515+
/// use icu::datetime::neo::TypedNeoFormatter;
2516+
/// use icu::datetime::neo_marker::NeoTimeZoneSpecificShortMarker;
2517+
/// use icu::datetime::neo_skeleton::NeoSkeletonLength;
2518+
/// use icu::datetime::LoadError;
2519+
/// use icu::locale::locale;
2520+
///
2521+
/// let result = TypedNeoFormatter::<Gregorian, NeoTimeZoneSpecificShortMarker>::try_new(
2522+
/// &locale!("en").into(),
2523+
/// NeoSkeletonLength::Long.into(),
2524+
/// );
2525+
///
2526+
/// assert!(matches!(result, Err(LoadError::TypeTooNarrow(_))));
2527+
/// ```
2528+
NeoTimeZoneSpecificShortMarker,
2529+
NeoTimeZoneSkeleton::specific(),
2530+
description = "specific time zone (only short), or raw offset if unavailable",
2531+
sample_length = Short,
2532+
sample = "CDT",
24752533
zone_essentials = yes,
2476-
zone_specific_long = yes,
2534+
zone_specific_short = yes,
24772535
);
24782536

24792537
impl_zone_marker!(
24802538
NeoTimeZoneOffsetMarker,
24812539
NeoTimeZoneSkeleton::offset(),
2482-
description = "UTC offset with inherited length",
2540+
description = "UTC offset time zone",
24832541
sample_length = Medium,
24842542
sample = "GMT-05:00", // TODO: Implement short localized offset
24852543
zone_essentials = yes,
24862544
);
24872545

2488-
impl_zone_marker!(
2489-
NeoTimeZoneOffsetShortMarker,
2490-
NeoTimeZoneSkeleton::offset_short(),
2491-
description = "UTC offset with a shorter length",
2492-
sample = "GMT-05:00", // TODO: Implement short localized offset
2493-
zone_essentials = yes,
2494-
);
2495-
2496-
impl_zone_marker!(
2497-
NeoTimeZoneOffsetLongMarker,
2498-
NeoTimeZoneSkeleton::offset_long(),
2499-
description = "UTC offset with a longer length",
2500-
sample = "GMT-05:00",
2501-
zone_essentials = yes,
2502-
);
2503-
2504-
impl_zone_marker!(
2505-
NeoTimeZoneGenericMarker,
2506-
NeoTimeZoneSkeleton::generic(),
2507-
description = "generic time zone with inherited length, or location if unavailable",
2508-
sample_length = Medium,
2509-
sample = "CT",
2510-
zone_essentials = yes,
2511-
zone_exemplar_cities = yes,
2512-
zone_generic_short = yes,
2513-
);
2514-
25152546
impl_zone_marker!(
25162547
/// When a display name is unavailable, falls back to the location format:
25172548
///
@@ -2520,15 +2551,15 @@ impl_zone_marker!(
25202551
/// use icu::timezone::{CustomTimeZone, CustomZonedDateTime};
25212552
/// use icu::calendar::Gregorian;
25222553
/// use icu::datetime::neo::TypedNeoFormatter;
2523-
/// use icu::datetime::neo_marker::NeoTimeZoneGenericShortMarker;
2554+
/// use icu::datetime::neo_marker::NeoTimeZoneGenericMarker;
25242555
/// use icu::datetime::neo_skeleton::NeoSkeletonLength;
25252556
/// use icu::locale::locale;
25262557
/// use tinystr::tinystr;
25272558
/// use writeable::assert_try_writeable_eq;
25282559
///
2529-
/// let fmt = TypedNeoFormatter::<Gregorian, NeoTimeZoneGenericShortMarker>::try_new(
2560+
/// let fmt = TypedNeoFormatter::<Gregorian, NeoTimeZoneGenericMarker>::try_new(
25302561
/// &locale!("en").into(),
2531-
/// Default::default(),
2562+
/// NeoSkeletonLength::Short.into(),
25322563
/// )
25332564
/// .unwrap();
25342565
///
@@ -2545,29 +2576,86 @@ impl_zone_marker!(
25452576
/// "Sao Paulo Time"
25462577
/// );
25472578
/// ```
2548-
NeoTimeZoneGenericShortMarker,
2549-
NeoTimeZoneSkeleton::generic_short(),
2550-
description = "generic time zone with a shorter length, or location if unavailable",
2551-
sample = "CT",
2579+
NeoTimeZoneGenericMarker,
2580+
NeoTimeZoneSkeleton::generic(),
2581+
description = "generic time zone, or location if unavailable",
2582+
sample_length = Long,
2583+
sample = "Central Time",
25522584
zone_essentials = yes,
25532585
zone_exemplar_cities = yes,
2586+
zone_generic_long = yes,
25542587
zone_generic_short = yes,
25552588
);
25562589

25572590
impl_zone_marker!(
2558-
NeoTimeZoneGenericLongMarker,
2559-
NeoTimeZoneSkeleton::generic_long(),
2560-
description = "generic time zone with a longer length, or location if unavailable",
2561-
sample = "Central Time",
2591+
/// This marker only loads data for the short length. Useful when combined with other fields:
2592+
///
2593+
/// ```
2594+
/// use icu::calendar::{Date, Time};
2595+
/// use icu::timezone::{CustomTimeZone, CustomZonedDateTime};
2596+
/// use icu::calendar::Gregorian;
2597+
/// use icu::datetime::neo::NeoFormatter;
2598+
/// use icu::datetime::neo_marker::NeoMonthDayMarker;
2599+
/// use icu::datetime::neo_marker::NeoHourMinuteMarker;
2600+
/// use icu::datetime::neo_marker::NeoTimeZoneGenericShortMarker;
2601+
/// use icu::datetime::neo_marker::DateTimeCombo;
2602+
/// use icu::datetime::neo_skeleton::NeoSkeletonLength;
2603+
/// use icu::locale::locale;
2604+
/// use tinystr::tinystr;
2605+
/// use writeable::assert_try_writeable_eq;
2606+
///
2607+
/// type MyDateTimeZoneSet = DateTimeCombo<
2608+
/// NeoMonthDayMarker,
2609+
/// NeoHourMinuteMarker,
2610+
/// NeoTimeZoneGenericShortMarker,
2611+
/// >;
2612+
///
2613+
/// let fmt = NeoFormatter::<MyDateTimeZoneSet>::try_new(
2614+
/// &locale!("en-US").into(),
2615+
/// NeoSkeletonLength::Long.into(),
2616+
/// )
2617+
/// .unwrap();
2618+
///
2619+
/// let dtz = CustomZonedDateTime::try_from_str("2024-09-17T15:47:50-05:00[America/Chicago]").unwrap();
2620+
///
2621+
/// assert_try_writeable_eq!(
2622+
/// fmt.convert_and_format(&dtz),
2623+
/// "September 17, 3:47 PM CT"
2624+
/// );
2625+
/// ```
2626+
///
2627+
/// Don't use long length if it is the only field:
2628+
///
2629+
/// ```
2630+
/// use icu::calendar::Gregorian;
2631+
/// use icu::datetime::neo::TypedNeoFormatter;
2632+
/// use icu::datetime::neo_marker::NeoTimeZoneGenericShortMarker;
2633+
/// use icu::datetime::neo_skeleton::NeoSkeletonLength;
2634+
/// use icu::datetime::LoadError;
2635+
/// use icu::locale::locale;
2636+
///
2637+
/// let result = TypedNeoFormatter::<Gregorian, NeoTimeZoneGenericShortMarker>::try_new(
2638+
/// &locale!("en").into(),
2639+
/// NeoSkeletonLength::Long.into(),
2640+
/// );
2641+
///
2642+
/// assert!(matches!(result, Err(LoadError::TypeTooNarrow(_))));
2643+
/// ```
2644+
NeoTimeZoneGenericShortMarker,
2645+
NeoTimeZoneSkeleton::generic(),
2646+
description = "generic time zone (only short), or location if unavailable",
2647+
sample_length = Short,
2648+
sample = "CT",
25622649
zone_essentials = yes,
25632650
zone_exemplar_cities = yes,
2564-
zone_generic_long = yes,
2651+
zone_generic_short = yes,
25652652
);
25662653

25672654
impl_zone_marker!(
25682655
NeoTimeZoneLocationMarker,
25692656
NeoTimeZoneSkeleton::location(),
25702657
description = "location time zone",
2658+
sample_length = Long,
25712659
sample = "Chicago Time",
25722660
zone_essentials = yes,
25732661
zone_exemplar_cities = yes,
@@ -2581,7 +2669,7 @@ impl_zoneddatetime_marker!(
25812669
sample = "May 17, 2024, 3:47:50 PM GMT",
25822670
date = NeoAutoDateMarker,
25832671
time = NeoAutoTimeMarker,
2584-
zone = NeoTimeZoneGenericShortMarker,
2672+
zone = NeoTimeZoneGenericMarker,
25852673
);
25862674

25872675
/// Trait for components that can be formatted at runtime.

0 commit comments

Comments
 (0)