Skip to content

Commit a572320

Browse files
Make some ZonedDateTime<Iso, UtcOffset> functions generic in calendar (#7630)
I don't know why we only had these for the `Iso` calendar, there's nothing stopping us from having them for all calendars, because they only need `.to_rata_die()`. ## Changelog icu_time: * Make some `ZonedDateTime<Iso, UtcOffset>` functions generic in calendar
1 parent 8a1ccbf commit a572320

File tree

14 files changed

+223
-40
lines changed

14 files changed

+223
-40
lines changed

components/datetime/src/time_crate.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -265,12 +265,7 @@ impl GetField<Option<UtcOffset>> for time::OffsetDateTime {
265265
impl GetField<ZoneNameTimestamp> for time::OffsetDateTime {
266266
#[inline]
267267
fn get_field(&self) -> ZoneNameTimestamp {
268-
ZoneNameTimestamp::from_zoned_date_time_iso(
269-
ZonedDateTime::from_epoch_milliseconds_and_utc_offset(
270-
self.unix_timestamp(),
271-
UtcOffset::zero(),
272-
),
273-
)
268+
ZoneNameTimestamp::from_epoch_seconds(self.unix_timestamp())
274269
}
275270
}
276271

components/time/src/types.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ where
312312

313313
const UNIX_EPOCH: RataDie = calendrical_calculations::gregorian::fixed_from_gregorian(1970, 1, 1);
314314

315-
impl Ord for ZonedDateTime<Iso, UtcOffset> {
315+
impl<A: AsCalendar> Ord for ZonedDateTime<A, UtcOffset> {
316316
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
317317
let mut srd = self.date.to_rata_die();
318318
let mut ord = other.date.to_rata_die();
@@ -357,8 +357,13 @@ impl Ord for ZonedDateTime<Iso, UtcOffset> {
357357
.then(self.time.subsecond.cmp(&other.time.subsecond))
358358
}
359359
}
360-
impl PartialOrd for ZonedDateTime<Iso, UtcOffset> {
361-
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
360+
361+
impl<A> PartialOrd<ZonedDateTime<A, UtcOffset>> for ZonedDateTime<A, UtcOffset>
362+
where
363+
A: AsCalendar,
364+
Date<A>: PartialEq<Date<A>>,
365+
{
366+
fn partial_cmp(&self, other: &ZonedDateTime<A, UtcOffset>) -> Option<core::cmp::Ordering> {
362367
Some(self.cmp(other))
363368
}
364369
}

components/time/src/zone/mod.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ pub mod windows;
5353
mod zone_name_timestamp;
5454

5555
use icu_calendar::types::RataDie;
56+
use icu_calendar::AsCalendar;
5657
#[cfg(feature = "compiled_data")]
5758
use icu_locale_core::subtags::Region;
5859
#[doc(inline)]
@@ -285,8 +286,8 @@ impl<'a> zerovec::maps::ZeroMapKV<'a> for TimeZone {
285286
/// );
286287
///
287288
/// // ... or by adding a local time
288-
/// let time_zone_at_time = time_zone.at_date_time_iso(DateTime {
289-
/// date: Date::try_new_iso(2023, 12, 2).unwrap(),
289+
/// let time_zone_at_time = time_zone.at_date_time(DateTime {
290+
/// date: Date::try_new_coptic(1996, 12, 2).unwrap(),
290291
/// time: Time::start_of_day(),
291292
/// });
292293
/// ```
@@ -451,16 +452,25 @@ impl TimeZoneInfo<models::Base> {
451452

452453
/// Sets the [`ZoneNameTimestamp`] to the given datetime.
453454
///
454-
/// If the offset is knonw, the datetime is interpreted as a local time,
455+
/// If the offset is known, the datetime is interpreted as a local time,
455456
/// otherwise as UTC. This produces correct results for the vast majority
456457
/// of cases, however close to metazone changes (Eastern Time -> Central Time)
457458
/// it might be incorrect if the offset is not known.
458459
///
459460
/// Also see [`Self::with_zone_name_timestamp`].
460-
pub fn at_date_time_iso(self, date_time: DateTime<Iso>) -> TimeZoneInfo<models::AtTime> {
461+
pub fn at_date_time<C: AsCalendar>(
462+
self,
463+
date_time: DateTime<C>,
464+
) -> TimeZoneInfo<models::AtTime> {
461465
self.at_rd_time(date_time.date.to_rata_die(), date_time.time)
462466
}
463467

468+
/// Use [`Self::at_date_time`].
469+
#[deprecated(since = "2.2.0", note = "use `Self::at_date_time`")]
470+
pub fn at_date_time_iso(self, date_time: DateTime<Iso>) -> TimeZoneInfo<models::AtTime> {
471+
self.at_date_time(date_time)
472+
}
473+
464474
pub(crate) fn at_rd_time(self, rd: RataDie, time: Time) -> TimeZoneInfo<models::AtTime> {
465475
self.with_zone_name_timestamp(ZoneNameTimestamp::from_rd_time_zone(
466476
rd,

components/time/src/zone/zone_name_timestamp.rs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
use core::fmt;
66

77
use icu_calendar::types::RataDie;
8-
use icu_calendar::Iso;
8+
use icu_calendar::{AsCalendar, Iso};
99
use zerovec::ule::AsULE;
1010

1111
use crate::Time;
@@ -84,7 +84,7 @@ impl ZoneNameTimestamp {
8484
/// zone: UtcOffset::zero(),
8585
/// };
8686
///
87-
/// let zone_name_timestamp = ZoneNameTimestamp::from_zoned_date_time_iso(zoned_date_time);
87+
/// let zone_name_timestamp = ZoneNameTimestamp::from_zoned_date_time(zoned_date_time);
8888
///
8989
/// let recovered_zoned_date_time = zone_name_timestamp.to_zoned_date_time_iso();
9090
///
@@ -106,14 +106,22 @@ impl ZoneNameTimestamp {
106106
}
107107

108108
/// Creates an instance of [`ZoneNameTimestamp`] from a [`ZonedDateTime`] with an explicit [`UtcOffset`].
109-
pub fn from_zoned_date_time_iso(zoned_date_time: ZonedDateTime<Iso, UtcOffset>) -> Self {
109+
pub fn from_zoned_date_time<C: AsCalendar>(
110+
zoned_date_time: ZonedDateTime<C, UtcOffset>,
111+
) -> Self {
110112
Self::from_rd_time_zone(
111113
zoned_date_time.date.to_rata_die(),
112114
zoned_date_time.time,
113115
zoned_date_time.zone,
114116
)
115117
}
116118

119+
/// Use [`Self::from_zoned_date_time`].
120+
#[deprecated(since = "2.2.0", note = "use `Self::from_zoned_date_time`")]
121+
pub fn from_zoned_date_time_iso(zoned_date_time: ZonedDateTime<Iso, UtcOffset>) -> Self {
122+
Self::from_zoned_date_time(zoned_date_time)
123+
}
124+
117125
pub(crate) fn from_rd_time_zone(rd: RataDie, time: Time, zone: UtcOffset) -> Self {
118126
Self::from_epoch_seconds(
119127
(rd - RD_EPOCH) * 24 * 60 * 60 + time.seconds_since_midnight() as i64
@@ -186,7 +194,7 @@ impl ZoneNameTimestamp {
186194
note = "implicitly interprets the DateTime as UTC. Use `from_zoned_date_time_iso` instead."
187195
)]
188196
pub fn from_date_time_iso(DateTime { date, time }: DateTime<Iso>) -> Self {
189-
Self::from_zoned_date_time_iso(ZonedDateTime {
197+
Self::from_zoned_date_time(ZonedDateTime {
190198
date,
191199
time,
192200
zone: UtcOffset::zero(),
@@ -280,7 +288,7 @@ impl<'de> serde::Deserialize<'de> for ZoneNameTimestamp {
280288
let day = parts[8..10].parse::<u8>().map_err(e1)?;
281289
let hour = parts[11..13].parse::<u8>().map_err(e1)?;
282290
let minute = parts[14..16].parse::<u8>().map_err(e1)?;
283-
return Ok(Self::from_zoned_date_time_iso(ZonedDateTime {
291+
return Ok(Self::from_zoned_date_time(ZonedDateTime {
284292
date: icu_calendar::Date::try_new_iso(year, month, day).map_err(e2)?,
285293
time: Time::try_new(hour, minute, 0, 0).map_err(e3)?,
286294
zone: UtcOffset::zero(),
@@ -360,7 +368,7 @@ mod test {
360368
output: "2025-04-30T15:15Z",
361369
},
362370
] {
363-
let znt = ZoneNameTimestamp::from_zoned_date_time_iso(
371+
let znt = ZoneNameTimestamp::from_zoned_date_time(
364372
ZonedDateTime::try_offset_only_from_str(test_case.input, Iso).unwrap(),
365373
);
366374
let actual = znt.to_zoned_date_time_iso();

ffi/capi/bindings/c/TimeZoneInfo.h

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ffi/capi/bindings/cpp/icu4x/TimeZoneInfo.d.hpp

Lines changed: 20 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ffi/capi/bindings/cpp/icu4x/TimeZoneInfo.hpp

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ffi/capi/src/timezone.rs

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ pub mod ffi {
1010
use alloc::boxed::Box;
1111

1212
use crate::unstable::{
13-
date::ffi::IsoDate, datetime::ffi::IsoDateTime, time::ffi::Time,
13+
date::ffi::{Date, IsoDate},
14+
datetime::ffi::IsoDateTime,
15+
time::ffi::Time,
1416
variant_offset::ffi::UtcOffset,
1517
};
1618

@@ -180,7 +182,7 @@ pub mod ffi {
180182
/// - If the offset is not set, the datetime is interpreted as UTC.
181183
/// - The constraints are the same as with `ZoneNameTimestamp` in Rust.
182184
/// - Set to year 1000 or 9999 for a reference far in the past or future.
183-
#[diplomat::rust_link(icu::time::TimeZoneInfo::at_date_time_iso, FnInStruct)]
185+
#[diplomat::rust_link(icu::time::TimeZoneInfo::at_date_time, FnInStruct)]
184186
#[diplomat::rust_link(icu::time::zone::ZoneNameTimestamp, Struct, compact)]
185187
#[diplomat::rust_link(
186188
icu::time::TimeZoneInfo::with_zone_name_timestamp,
@@ -208,7 +210,7 @@ pub mod ffi {
208210
zone_name_timestamp: Some(
209211
self.id
210212
.with_offset(self.offset)
211-
.at_date_time_iso(icu_time::DateTime {
213+
.at_date_time(icu_time::DateTime {
212214
date: date.0,
213215
time: time.0,
214216
})
@@ -218,6 +220,53 @@ pub mod ffi {
218220
})
219221
}
220222

223+
/// Sets the datetime at which to interpret the time zone
224+
/// for display name lookup.
225+
///
226+
/// Notes:
227+
///
228+
/// - If not set, the formatting datetime is used if possible.
229+
/// - If the offset is not set, the datetime is interpreted as UTC.
230+
/// - The constraints are the same as with `ZoneNameTimestamp` in Rust.
231+
/// - Set to year 1000 or 9999 for a reference far in the past or future.
232+
#[diplomat::rust_link(icu::time::TimeZoneInfo::at_date_time, FnInStruct)]
233+
#[diplomat::rust_link(icu::time::zone::ZoneNameTimestamp, Struct, compact)]
234+
#[diplomat::rust_link(
235+
icu::time::TimeZoneInfo::with_zone_name_timestamp,
236+
FnInStruct,
237+
hidden
238+
)]
239+
#[diplomat::rust_link(
240+
icu::time::zone::ZoneNameTimestamp::from_date_time,
241+
FnInStruct,
242+
hidden
243+
)]
244+
#[diplomat::rust_link(
245+
icu::time::zone::ZoneNameTimestamp::from_zoned_date_time,
246+
FnInStruct,
247+
hidden
248+
)]
249+
#[diplomat::rust_link(
250+
icu::time::zone::ZoneNameTimestamp::far_in_future,
251+
FnInStruct,
252+
hidden
253+
)] // documented
254+
#[diplomat::rust_link(icu::time::zone::ZoneNameTimestamp::far_in_past, FnInStruct, hidden)] // documented
255+
pub fn at_date_time(&self, date: &Date, time: &Time) -> Box<Self> {
256+
Box::new(Self {
257+
zone_name_timestamp: Some(
258+
self.id
259+
.with_offset(self.offset)
260+
.at_date_time(icu_time::DateTime {
261+
date: date.0.clone(),
262+
time: time.0,
263+
})
264+
.zone_name_timestamp(),
265+
),
266+
..*self
267+
})
268+
}
269+
221270
/// Sets the timestamp, in milliseconds since Unix epoch, at which to interpret the time zone
222271
/// for display name lookup.
223272
///
@@ -308,8 +357,8 @@ impl ffi::TimeZoneInfo {
308357
if let Some(zone_name_timestamp) = self.zone_name_timestamp {
309358
base.with_zone_name_timestamp(zone_name_timestamp)
310359
} else if let Some(date) = date {
311-
base.at_date_time_iso(icu_time::DateTime {
312-
date: date.to_calendar(icu_calendar::Iso),
360+
base.at_date_time(icu_time::DateTime {
361+
date,
313362
time: time.unwrap_or(icu_time::Time::noon()),
314363
})
315364
} else {

ffi/capi/src/variant_offset.rs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -181,16 +181,6 @@ pub mod ffi {
181181
icu::time::zone::VariantOffsetsCalculatorBorrowed::compute_offsets_from_time_zone_and_name_timestamp,
182182
FnInStruct
183183
)]
184-
#[diplomat::rust_link(
185-
icu::time::zone::ZoneNameTimestamp::from_zoned_date_time_iso,
186-
FnInStruct,
187-
hidden
188-
)]
189-
#[diplomat::rust_link(
190-
icu::time::ZonedDateTime::from_epoch_milliseconds_and_utc_offset,
191-
FnInStruct,
192-
hidden
193-
)]
194184
pub fn compute_offsets_from_time_zone_and_timestamp(
195185
&self,
196186
time_zone: &TimeZone,

ffi/dart/lib/src/bindings/TimeZoneInfo.g.dart

Lines changed: 24 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)