Skip to content

Commit

Permalink
convert metazone period from string to i32 (unicode-org#2085)
Browse files Browse the repository at this point in the history
* convert metazone period from string to i32

* address comments

* address minor comments

* remove unnecssary import

* fix test

* make testdata
  • Loading branch information
samchen61661 committed Jul 12, 2022
1 parent c80391a commit 330b4cf
Show file tree
Hide file tree
Showing 28 changed files with 13,136 additions and 13,889 deletions.
2 changes: 1 addition & 1 deletion components/calendar/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ impl std::error::Error for DateTimeError {}

/// A list of possible error outcomes for working with various inputs to DateTime inputs
/// and operations.
#[derive(Display, Debug, Copy, Clone)]
#[derive(Display, Debug, Copy, Clone, PartialEq)]
#[non_exhaustive]
pub enum DateTimeError {
/// An input could not be parsed.
Expand Down
74 changes: 74 additions & 0 deletions components/calendar/src/iso.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,80 @@ impl DateTime<Iso> {
time: types::Time::try_new(hour, minute, second, 0)?,
})
}

/// Minute count representation of calendars starting from 00:00:00 on Jan 1st, 1970.
///
/// ```rust
/// use icu::calendar::DateTime;
///
/// let today = DateTime::new_iso_datetime(2020, 2, 29, 0, 0, 0).unwrap();
///
/// assert_eq!(today.minutes_since_local_unix_epoch(), 26382240);
/// assert_eq!(
/// DateTime::from_minutes_since_local_unix_epoch(26382240),
/// Ok(today)
/// );
///
/// let today = DateTime::new_iso_datetime(1970, 1, 1, 0, 0, 0).unwrap();
///
/// assert_eq!(today.minutes_since_local_unix_epoch(), 0);
/// assert_eq!(DateTime::from_minutes_since_local_unix_epoch(0), Ok(today));
/// ```
pub fn minutes_since_local_unix_epoch(&self) -> i32 {
let minutes_a_hour = 60;
let hours_a_day = 24;
let minutes_a_day = minutes_a_hour * hours_a_day;
if let Ok(unix_epoch) = DateTime::new_iso_datetime(1970, 1, 1, 0, 0, 0) {
(Iso::fixed_from_iso(*self.date.inner())
- Iso::fixed_from_iso(*unix_epoch.date.inner()))
* minutes_a_day
+ i32::from(self.time.hour.number()) * minutes_a_hour
+ i32::from(self.time.minute.number())
} else {
0
}
}

/// Convert minute count since 00:00:00 on Jan 1st, 1970 to ISO Date.
///
/// ```rust
/// use icu::calendar::DateTime;
///
/// let today = DateTime::new_iso_datetime(2020, 2, 29, 0, 0, 0).unwrap();
///
/// assert_eq!(today.minutes_since_local_unix_epoch(), 26382240);
/// assert_eq!(
/// DateTime::from_minutes_since_local_unix_epoch(26382240),
/// Ok(today)
/// );
///
/// let today = DateTime::new_iso_datetime(1970, 1, 1, 0, 0, 0).unwrap();
///
/// assert_eq!(today.minutes_since_local_unix_epoch(), 0);
/// assert_eq!(DateTime::from_minutes_since_local_unix_epoch(0), Ok(today));
/// ```
pub fn from_minutes_since_local_unix_epoch(
minute: i32,
) -> Result<DateTime<Iso>, DateTimeError> {
let minutes_a_hour = 60;
let hours_a_day = 24;
let minutes_a_day = minutes_a_hour * hours_a_day;
let extra_days = minute / minutes_a_day;
if let Ok(unix_epoch) = DateTime::new_iso_datetime(1970, 1, 1, 0, 0, 0) {
let unix_epoch_days = Iso::fixed_from_iso(*unix_epoch.date.inner());
let date = Iso::iso_from_fixed(unix_epoch_days + extra_days);
DateTime::new_iso_datetime(
date.year().number,
date.month().ordinal as u8,
date.day_of_month().0 as u8,
((minute / minutes_a_hour) % hours_a_day) as u8,
(minute % minutes_a_hour) as u8,
0,
)
} else {
unreachable!("DateTime should be created successfully")
}
}
}

impl Iso {
Expand Down
4 changes: 2 additions & 2 deletions components/datetime/src/provider/time_zones.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ pub struct MetaZoneSpecificNamesShortV1<'data> {
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
#[yoke(prove_covariance_manually)]
pub struct MetaZonePeriodV1<'data>(
/// The default mapping between period and metazone id. The second level key is a timestamp string. It represents when the metazone started to be used.
/// The default mapping between period and metazone id. The second level key is a wall-clock time represented as the number of minutes since the local unix epoch. It represents when the metazone started to be used.
#[cfg_attr(feature = "serde", serde(borrow))]
pub ZeroMap2d<'data, TimeZoneBcp47Id, str, Option<MetaZoneId>>,
pub ZeroMap2d<'data, TimeZoneBcp47Id, i32, Option<MetaZoneId>>,
);
37 changes: 24 additions & 13 deletions provider/datagen/src/transform/cldr/time_zones/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).

use icu_calendar::DateTime;
use icu_datetime::provider::time_zones::{
ExemplarCitiesV1, MetaZoneGenericNamesLongV1, MetaZoneGenericNamesShortV1, MetaZoneId,
MetaZonePeriodV1, MetaZoneSpecificNamesLongV1, MetaZoneSpecificNamesShortV1, TimeZoneBcp47Id,
Expand Down Expand Up @@ -444,47 +445,57 @@ fn metazone_periods_iter(
Vec<MetaZoneForPeriod>,
LiteMap<String, MetaZoneId>,
),
) -> impl Iterator<Item = (TimeZoneBcp47Id, String, Option<MetaZoneId>)> {
) -> impl Iterator<Item = (TimeZoneBcp47Id, i32, Option<MetaZoneId>)> {
let (time_zone_key, periods, meta_zone_id_data) = pair;
periods
.into_iter()
.map(move |period| match &period.uses_meta_zone.from {
Some(from) => {
// TODO(#2127): Ideally this parsing can move into a library function
let parts: Vec<String> = from.split(' ').map(|s| s.to_string()).collect();
let date = &parts[0];
let time = &parts[1];
let date_parts: Vec<String> = date.split('-').map(|s| s.to_string()).collect();
let year = date_parts[0].parse::<i32>().unwrap();
let month = date_parts[1].parse::<u8>().unwrap();
let day = date_parts[2].parse::<u8>().unwrap();
let time_parts: Vec<String> = time.split(':').map(|s| s.to_string()).collect();
let hour = time_parts[0].parse::<u8>().unwrap();
let minute = time_parts[1].parse::<u8>().unwrap();
let iso = DateTime::new_iso_datetime(year, month, day, hour, minute, 0).unwrap();
let minutes = iso.minutes_since_local_unix_epoch();

match meta_zone_id_data.get(&period.uses_meta_zone.mzone) {
Some(meta_zone_short_id) => {
(time_zone_key, from.clone(), Some(*meta_zone_short_id))
}
Some(meta_zone_short_id) => (time_zone_key, minutes, Some(*meta_zone_short_id)),
None => {
// TODO(#1781): Remove this special case once the short id is updated in CLDR
if &period.uses_meta_zone.mzone == "Yukon" {
(
time_zone_key,
from.clone(),
minutes,
Some(MetaZoneId(tinystr::tinystr!(4, "yuko"))),
)
} else {
(time_zone_key, from.clone(), None)
(time_zone_key, minutes, None)
}
}
}
}
None => {
let iso = DateTime::new_iso_datetime(1970, 1, 1, 0, 0, 0).unwrap();
let minutes = iso.minutes_since_local_unix_epoch();
match meta_zone_id_data.get(&period.uses_meta_zone.mzone) {
Some(meta_zone_short_id) => (
time_zone_key,
String::from("1970-00-00 00:00"),
Some(*meta_zone_short_id),
),
Some(meta_zone_short_id) => (time_zone_key, minutes, Some(*meta_zone_short_id)),
None => {
// TODO(#1781): Remove this special case once the short id is updated in CLDR
if &period.uses_meta_zone.mzone == "Yukon" {
(
time_zone_key,
String::from("1970-00-00 00:00"),
minutes,
Some(MetaZoneId(tinystr::tinystr!(4, "yuko"))),
)
} else {
(time_zone_key, String::from("1970-00-00 00:00"), None)
(time_zone_key, minutes, None)
}
}
}
Expand Down
5 changes: 1 addition & 4 deletions provider/datagen/src/transform/cldr/time_zones/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -277,10 +277,7 @@ mod tests {
metazone_period
.get()
.0
.get_copied(
&TimeZoneBcp47Id(tinystr!(8, "gblon")),
&String::from("1971-10-31 02:00")
)
.get_copied(&TimeZoneBcp47Id(tinystr!(8, "gblon")), &962040)
.unwrap()
);
}
Expand Down
Loading

0 comments on commit 330b4cf

Please sign in to comment.