Skip to content

Commit 2a0b6f3

Browse files
authored
Add type ZoneNameTimestamp for time zone display names (#6513)
Toward #6238 Replaces #6322 Fixes #6235
1 parent 3e3da0a commit 2a0b6f3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+2075
-1557
lines changed

Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

components/datetime/benches/datetime.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,10 @@ fn datetime_benches(c: &mut Criterion) {
3535
time,
3636
// zone is unused but we need to make the types match
3737
zone: TimeZoneInfo::utc()
38-
.at_time((Date::try_new_iso(2024, 1, 1).unwrap(), Time::start_of_day()))
38+
.at_date_time_iso(&DateTime {
39+
date: Date::try_new_iso(2024, 1, 1).unwrap(),
40+
time: Time::start_of_day(),
41+
})
3942
.with_variant(TimeZoneVariant::Standard),
4043
}
4144
}

components/datetime/examples/timezone_picker.rs

+14-5
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ use std::collections::BTreeMap;
77
use icu::calendar::Date;
88
use icu::datetime::{fieldsets, NoCalendarFormatter};
99
use icu::locale::locale;
10-
use icu::time::Time;
10+
use icu::time::{DateTime, Time};
11+
use icu_time::zone::ZoneNameTimestamp;
1112
use icu_time::TimeZone;
1213

1314
fn main() {
@@ -23,7 +24,10 @@ fn main() {
2324
let city_formatter =
2425
NoCalendarFormatter::try_new(prefs, fieldsets::zone::ExemplarCity).unwrap();
2526

26-
let reference_date = (Date::try_new_iso(2025, 1, 1).unwrap(), Time::start_of_day());
27+
let reference_date_time = DateTime {
28+
date: Date::try_new_iso(2025, 1, 1).unwrap(),
29+
time: Time::start_of_day(),
30+
};
2731

2832
let mut grouped_tzs = BTreeMap::<_, Vec<_>>::new();
2933

@@ -33,12 +37,15 @@ fn main() {
3337
}
3438

3539
let offsets = offsets
36-
.compute_offsets_from_time_zone(tz, reference_date)
40+
.compute_offsets_from_time_zone(
41+
tz,
42+
ZoneNameTimestamp::from_date_time_iso(&reference_date_time),
43+
)
3744
.unwrap();
3845

3946
let tzi = tz
4047
.with_offset(Some(offsets.standard))
41-
.at_time(reference_date);
48+
.at_date_time_iso(&reference_date_time);
4249

4350
grouped_tzs
4451
.entry(non_location_formatter.format(&tzi).to_string())
@@ -59,7 +66,9 @@ fn main() {
5966
format!(
6067
"/{}",
6168
offset_formatter.format(
62-
&tzi.id().with_offset(Some(daylight)).at_time(reference_date)
69+
&tzi.id()
70+
.with_offset(Some(daylight))
71+
.at_date_time_iso(&reference_date_time)
6372
)
6473
)
6574
} else {

components/datetime/src/fieldsets.rs

+20-27
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,11 @@ use crate::{
6363
scaffold::*,
6464
};
6565
use enums::*;
66-
use icu_calendar::{
67-
types::{DayOfMonth, MonthInfo, Weekday, YearInfo},
68-
Date, Iso,
69-
};
66+
use icu_calendar::types::{DayOfMonth, MonthInfo, Weekday, YearInfo};
7067
use icu_provider::marker::NeverMarker;
7168
use icu_time::{
72-
zone::{TimeZoneVariant, UtcOffset},
73-
Hour, Minute, Nanosecond, Second, Time, TimeZone,
69+
zone::{TimeZoneVariant, UtcOffset, ZoneNameTimestamp},
70+
Hour, Minute, Nanosecond, Second, TimeZone,
7471
};
7572

7673
#[cfg(doc)]
@@ -933,8 +930,7 @@ macro_rules! impl_zone_marker {
933930
/// # Examples
934931
///
935932
/// ```
936-
/// use icu::datetime::input::Date;
937-
/// use icu::datetime::input::{Time, TimeZone,TimeZoneInfo, UtcOffset};
933+
/// use icu::datetime::input::{Date, DateTime, Time, TimeZone, TimeZoneInfo, UtcOffset};
938934
/// use icu::datetime::NoCalendarFormatter;
939935
/// use icu::time::zone::TimeZoneVariant;
940936
#[doc = concat!("use icu::datetime::fieldsets::zone::", stringify!($type), ";")]
@@ -950,7 +946,7 @@ macro_rules! impl_zone_marker {
950946
/// // Time zone info for America/Chicago in the summer
951947
/// let zone = TimeZone(subtag!("uschi"))
952948
/// .with_offset("-05".parse().ok())
953-
/// .at_time((Date::try_new_iso(2022, 8, 29).unwrap(), Time::start_of_day()))
949+
/// .at_date_time_iso(&DateTime{ date: Date::try_new_iso(2022, 8, 29).unwrap(), time: Time::start_of_day() })
954950
/// .with_variant(TimeZoneVariant::Daylight);
955951
///
956952
/// assert_writeable_eq!(
@@ -984,7 +980,7 @@ macro_rules! impl_zone_marker {
984980
type TimeZoneIdInput = datetime_marker_helper!(@input/timezone/id, $($tzid_input_yes)?);
985981
type TimeZoneOffsetInput = datetime_marker_helper!(@input/timezone/offset, yes);
986982
type TimeZoneVariantInput = datetime_marker_helper!(@input/timezone/variant, $($variant_input_yes)?);
987-
type TimeZoneLocalTimeInput = datetime_marker_helper!(@input/timezone/local_time, $($localtime_input_yes)?);
983+
type TimeZoneNameTimestampInput = datetime_marker_helper!(@input/timezone/local_time, $($localtime_input_yes)?);
988984
type EssentialsV1 = datetime_marker_helper!(@data/zone/essentials, $($zone_essentials_yes)?);
989985
type LocationsV1 = datetime_marker_helper!(@data/zone/locations, $($zone_locations_yes)?);
990986
type LocationsRootV1 = datetime_marker_helper!(@data/zone/locations_root, $($zone_locations_yes)?);
@@ -1281,8 +1277,7 @@ pub mod zone {
12811277
/// to the location format for long lengths:
12821278
///
12831279
/// ```
1284-
/// use icu::datetime::input::Date;
1285-
/// use icu::datetime::input::{Time, TimeZone, TimeZoneInfo, UtcOffset};
1280+
/// use icu::datetime::input::{Date, DateTime, Time, TimeZone, TimeZoneInfo, UtcOffset};
12861281
/// use icu::calendar::Gregorian;
12871282
/// use icu::datetime::FixedCalendarDateTimeFormatter;
12881283
/// use icu::datetime::fieldsets::zone::{SpecificLong, SpecificShort};
@@ -1293,7 +1288,7 @@ pub mod zone {
12931288
/// // Time zone info for Europe/Istanbul in the winter
12941289
/// let zone = TimeZone(subtag!("trist"))
12951290
/// .with_offset("+02".parse().ok())
1296-
/// .at_time((Date::try_new_iso(2022, 1, 29).unwrap(), Time::start_of_day()))
1291+
/// .at_date_time_iso(&DateTime{ date: Date::try_new_iso(2022, 1, 29).unwrap(), time: Time::start_of_day() })
12971292
/// .with_variant(TimeZoneVariant::Standard);
12981293
///
12991294
/// let fmt = FixedCalendarDateTimeFormatter::<Gregorian, _>::try_new(
@@ -1324,7 +1319,7 @@ pub mod zone {
13241319
/// For example, [`TimeZoneInfo<AtTime>`] cannot be formatted.
13251320
///
13261321
/// ```compile_fail,E0271
1327-
/// use icu::datetime::input::{Date, Iso};
1322+
/// use icu::datetime::input::{Date, DateTime, Iso};
13281323
/// use icu::datetime::FixedCalendarDateTimeFormatter;
13291324
/// use icu::datetime::fieldsets::zone::SpecificLong;
13301325
/// use icu::locale::{locale, subtags::subtag};
@@ -1334,7 +1329,7 @@ pub mod zone {
13341329
///
13351330
/// let datetime = DateTime { date: Date::try_new_gregorian(2024, 10, 18).unwrap(), time: Time::start_of_day() };
13361331
/// let time_zone_basic = TimeZone(subtag!("uschi")).with_offset("-06".parse().ok());
1337-
/// let time_zone_at_time = time_zone_basic.at_time((datetime.date.to_iso(), datetime.time));
1332+
/// let time_zone_at_time = time_zone_basic.at_date_time_iso(&DateTime{ date: datetime.date.to_iso(), time: datetime.time });
13381333
///
13391334
/// let formatter = FixedCalendarDateTimeFormatter::try_new(
13401335
/// locale!("en-US").into(),
@@ -1366,7 +1361,7 @@ pub mod zone {
13661361
/// For example, [`TimeZoneInfo<AtTime>`] cannot be formatted.
13671362
///
13681363
/// ```compile_fail,E0271
1369-
/// use icu::datetime::input::{Date, Iso};
1364+
/// use icu::datetime::input::{Date, DateTime, Iso};
13701365
/// use icu::datetime::FixedCalendarDateTimeFormatter;
13711366
/// use icu::datetime::fieldsets::{T, zone::SpecificShort};
13721367
/// use icu::locale::{locale, subtags::subtag};
@@ -1376,7 +1371,7 @@ pub mod zone {
13761371
///
13771372
/// let datetime = DateTime { Date::try_new_gregorian(2024, 10, 18).unwrap(), time: Time::start_of_day() };
13781373
/// let time_zone_basic = TimeZone(subtag!("uschi")).with_offset("-06".parse().ok());
1379-
/// let time_zone_at_time = time_zone_basic.at_time((datetime.date.to_iso(), datetime.time));
1374+
/// let time_zone_at_time = time_zone_basic.at_date_time_iso(&DateTime{ date: datetime.date.to_iso(), time: datetime.time });
13801375
///
13811376
/// let formatter = FixedCalendarDateTimeFormatter::try_new(
13821377
/// locale!("en-US").into(),
@@ -1408,7 +1403,7 @@ pub mod zone {
14081403
/// use icu::datetime::input::Date;
14091404
/// use icu::datetime::NoCalendarFormatter;
14101405
/// use icu::datetime::fieldsets::zone::LocalizedOffsetLong;
1411-
/// use icu::datetime::input::{Time, TimeZone, UtcOffset};
1406+
/// use icu::datetime::input::{DateTime, Time, TimeZone, UtcOffset};
14121407
/// use icu::time::zone::TimeZoneVariant;
14131408
/// use icu::locale::{locale, subtags::subtag};
14141409
/// use writeable::assert_writeable_eq;
@@ -1418,7 +1413,7 @@ pub mod zone {
14181413
///
14191414
/// let date = Date::try_new_iso(2024, 10, 18).unwrap();
14201415
/// let time = Time::start_of_day();
1421-
/// let time_zone_at_time = time_zone_basic.at_time((date, time));
1416+
/// let time_zone_at_time = time_zone_basic.at_date_time_iso(&DateTime{ date, time });
14221417
///
14231418
/// let time_zone_full = time_zone_at_time.with_variant(TimeZoneVariant::Standard);
14241419
///
@@ -1469,8 +1464,7 @@ pub mod zone {
14691464
/// When a display name is unavailable, falls back to the location format:
14701465
///
14711466
/// ```
1472-
/// use icu::datetime::input::Date;
1473-
/// use icu::datetime::input::{Time, TimeZone};
1467+
/// use icu::datetime::input::{Date, DateTime, Time, TimeZone};
14741468
/// use icu::calendar::Gregorian;
14751469
/// use icu::datetime::FixedCalendarDateTimeFormatter;
14761470
/// use icu::datetime::fieldsets::zone::GenericShort;
@@ -1480,7 +1474,7 @@ pub mod zone {
14801474
/// // Time zone info for Europe/Istanbul
14811475
/// let zone = TimeZone(subtag!("trist"))
14821476
/// .without_offset()
1483-
/// .at_time((Date::try_new_iso(2022, 1, 29).unwrap(), Time::start_of_day()));
1477+
/// .at_date_time_iso(&DateTime{ date: Date::try_new_iso(2022, 1, 29).unwrap(), time: Time::start_of_day() });
14841478
///
14851479
/// let fmt = FixedCalendarDateTimeFormatter::<Gregorian, _>::try_new(
14861480
/// locale!("en").into(),
@@ -1497,8 +1491,7 @@ pub mod zone {
14971491
/// Can also fall back to the UTC offset:
14981492
///
14991493
/// ```
1500-
/// use icu::datetime::input::Date;
1501-
/// use icu::datetime::input::Time;
1494+
/// use icu::datetime::input::{Date, DateTime, Time};
15021495
/// use icu::datetime::NoCalendarFormatter;
15031496
/// use icu::datetime::fieldsets::zone::GenericShort;
15041497
/// use icu::datetime::DateTimeWriteError;
@@ -1518,7 +1511,7 @@ pub mod zone {
15181511
/// let time_zone = IanaParser::new()
15191512
/// .parse("America/Chicago")
15201513
/// .with_offset("-05".parse().ok())
1521-
/// .at_time((Date::try_new_iso(2022, 8, 29).unwrap(), Time::start_of_day()));
1514+
/// .at_date_time_iso(&DateTime{ date: Date::try_new_iso(2022, 8, 29).unwrap(), time: Time::start_of_day() });
15221515
/// assert_writeable_eq!(
15231516
/// tzf.format(&time_zone),
15241517
/// "CT"
@@ -1528,7 +1521,7 @@ pub mod zone {
15281521
/// let time_zone = IanaParser::new()
15291522
/// .parse("Pacific/Honolulu")
15301523
/// .with_offset("-10".parse().ok())
1531-
/// .at_time((Date::try_new_iso(2022, 8, 29).unwrap(), Time::start_of_day()));
1524+
/// .at_date_time_iso(&DateTime{ date: Date::try_new_iso(2022, 8, 29).unwrap(), time: Time::start_of_day() });
15321525
/// assert_writeable_eq!(
15331526
/// tzf.format(&time_zone),
15341527
/// "HST"
@@ -1538,7 +1531,7 @@ pub mod zone {
15381531
/// let time_zone = IanaParser::new()
15391532
/// .parse("America/Chigagou")
15401533
/// .with_offset("-05".parse().ok())
1541-
/// .at_time((Date::try_new_iso(2022, 8, 29).unwrap(), Time::start_of_day()));
1534+
/// .at_date_time_iso(&DateTime{ date: Date::try_new_iso(2022, 8, 29).unwrap(), time: Time::start_of_day() });
15421535
/// assert_writeable_eq!(
15431536
/// tzf.format(&time_zone),
15441537
/// "GMT-5"

components/datetime/src/format/input.rs

+8-6
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@
77
88
use crate::scaffold::*;
99
use icu_calendar::types::DayOfYear;
10-
use icu_calendar::{AsCalendar, Calendar, Iso};
10+
use icu_calendar::{AsCalendar, Calendar};
1111
use icu_time::scaffold::IntoOption;
12+
use icu_time::zone::ZoneNameTimestamp;
1213
use icu_time::{zone::TimeZoneVariant, Hour, Minute, Nanosecond, Second};
1314

1415
use icu_calendar::Date;
@@ -56,7 +57,7 @@ pub struct DateTimeInputUnchecked {
5657
pub(crate) zone_variant: Option<TimeZoneVariant>,
5758
/// The local ISO time, required for field sets with
5859
/// certain time zone styles.
59-
pub(crate) zone_local_time: Option<(Date<Iso>, Time)>,
60+
pub(crate) zone_name_timestamp: Option<ZoneNameTimestamp>,
6061
}
6162

6263
impl DateTimeInputUnchecked {
@@ -94,8 +95,8 @@ impl DateTimeInputUnchecked {
9495
}
9596

9697
/// Sets the local time for time zone name resolution.
97-
pub fn set_time_zone_local_time(&mut self, local_time: (Date<Iso>, Time)) {
98-
self.zone_local_time = Some(local_time);
98+
pub fn set_time_zone_name_timestamp(&mut self, local_time: ZoneNameTimestamp) {
99+
self.zone_name_timestamp = Some(local_time);
99100
}
100101

101102
/// Sets the time zone variant.
@@ -122,7 +123,7 @@ impl DateTimeInputUnchecked {
122123
+ GetField<Z::TimeZoneIdInput>
123124
+ GetField<Z::TimeZoneOffsetInput>
124125
+ GetField<Z::TimeZoneVariantInput>
125-
+ GetField<Z::TimeZoneLocalTimeInput>,
126+
+ GetField<Z::TimeZoneNameTimestampInput>,
126127
{
127128
Self {
128129
year: GetField::<D::YearInput>::get_field(input).into_option(),
@@ -137,7 +138,8 @@ impl DateTimeInputUnchecked {
137138
zone_id: GetField::<Z::TimeZoneIdInput>::get_field(input).into_option(),
138139
zone_offset: GetField::<Z::TimeZoneOffsetInput>::get_field(input).into_option(),
139140
zone_variant: GetField::<Z::TimeZoneVariantInput>::get_field(input).into_option(),
140-
zone_local_time: GetField::<Z::TimeZoneLocalTimeInput>::get_field(input).into_option(),
141+
zone_name_timestamp: GetField::<Z::TimeZoneNameTimestampInput>::get_field(input)
142+
.into_option(),
141143
}
142144
}
143145
}

components/datetime/src/format/time_zone.rs

+14-12
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,27 @@ use crate::provider::time_zones::MetazoneId;
99
use crate::{format::DateTimeInputUnchecked, provider::fields::FieldLength};
1010
use core::fmt;
1111
use fixed_decimal::Decimal;
12-
use icu_calendar::{Date, Iso};
1312
use icu_decimal::DecimalFormatter;
14-
use icu_time::provider::MinutesSinceEpoch;
13+
use icu_time::zone::ZoneNameTimestamp;
1514
use icu_time::{
1615
zone::{TimeZoneVariant, UtcOffset},
17-
Time, TimeZone,
16+
TimeZone,
1817
};
1918
use writeable::Writeable;
2019

2120
impl crate::provider::time_zones::MetazonePeriod<'_> {
22-
fn resolve(&self, time_zone_id: TimeZone, dt: (Date<Iso>, Time)) -> Option<MetazoneId> {
21+
fn resolve(
22+
&self,
23+
time_zone_id: TimeZone,
24+
zone_name_timestamp: ZoneNameTimestamp,
25+
) -> Option<MetazoneId> {
2326
use zerovec::ule::AsULE;
2427
let cursor = self.list.get0(&time_zone_id)?;
2528
let mut metazone_id = None;
26-
let minutes_since_epoch_walltime = MinutesSinceEpoch::from(dt);
27-
for (minutes, id) in cursor.iter1() {
28-
if minutes_since_epoch_walltime >= MinutesSinceEpoch::from_unaligned(*minutes) {
29-
metazone_id = id.get()
30-
} else {
29+
for (bytes, id) in cursor.iter1().rev() {
30+
let candidate = ZoneNameTimestamp::from_unaligned(*bytes);
31+
if zone_name_timestamp >= candidate {
32+
metazone_id = id.get();
3133
break;
3234
}
3335
}
@@ -118,7 +120,7 @@ impl FormatTimeZone for GenericNonLocationFormat {
118120
let Some(time_zone_id) = input.zone_id else {
119121
return Ok(Err(FormatTimeZoneError::MissingInputField("time_zone_id")));
120122
};
121-
let Some(local_time) = input.zone_local_time else {
123+
let Some(local_time) = input.zone_name_timestamp else {
122124
return Ok(Err(FormatTimeZoneError::MissingInputField(
123125
"time_zone_local_time",
124126
)));
@@ -181,7 +183,7 @@ impl FormatTimeZone for SpecificNonLocationFormat {
181183
"time_zone_variant",
182184
)));
183185
};
184-
let Some(local_time) = input.zone_local_time else {
186+
let Some(local_time) = input.zone_name_timestamp else {
185187
return Ok(Err(FormatTimeZoneError::MissingInputField(
186188
"time_zone_local_time",
187189
)));
@@ -492,7 +494,7 @@ impl FormatTimeZone for GenericPartialLocationFormat {
492494
let Some(time_zone_id) = input.zone_id else {
493495
return Ok(Err(FormatTimeZoneError::MissingInputField("time_zone_id")));
494496
};
495-
let Some(local_time) = input.zone_local_time else {
497+
let Some(local_time) = input.zone_name_timestamp else {
496498
return Ok(Err(FormatTimeZoneError::MissingInputField(
497499
"time_zone_local_time",
498500
)));

components/datetime/src/provider/time_zones.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use icu_pattern::{DoublePlaceholderPattern, SinglePlaceholderPattern};
99
use icu_provider::prelude::*;
1010
use zerovec::{ule::NichedOption, ZeroMap, ZeroMap2d, ZeroVec};
1111

12-
use icu_time::{provider::MinutesSinceEpoch, zone::TimeZoneVariant, TimeZone};
12+
use icu_time::{zone::TimeZoneVariant, zone::ZoneNameTimestamp, TimeZone};
1313

1414
/// Time zone type aliases for cleaner code
1515
pub(crate) mod tz {
@@ -315,9 +315,9 @@ pub type MetazoneId = core::num::NonZeroU8;
315315
#[yoke(prove_covariance_manually)]
316316
pub struct MetazonePeriod<'data> {
317317
/// The default mapping between period and offsets. The second level key is a wall-clock time encoded as
318-
/// [`MinutesSinceEpoch`]. It represents when the metazone started to be used.
318+
/// [`ZoneNameTimestamp`]. It represents when the metazone started to be used.
319319
#[cfg_attr(feature = "serde", serde(borrow))]
320-
pub list: ZeroMap2d<'data, TimeZone, MinutesSinceEpoch, NichedOption<MetazoneId, 1>>,
320+
pub list: ZeroMap2d<'data, TimeZone, ZoneNameTimestamp, NichedOption<MetazoneId, 1>>,
321321
}
322322

323323
icu_provider::data_struct!(

0 commit comments

Comments
 (0)