Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove icu::calendar unix epoch APIs #5748

Merged
merged 1 commit into from
Oct 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 0 additions & 4 deletions components/calendar/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,6 @@ harness = false
name = "datetime"
harness = false

[[bench]]
name = "iso"
harness = false

[[bench]]
name = "convert"
harness = false
Expand Down
20 changes: 0 additions & 20 deletions components/calendar/benches/iso.rs

This file was deleted.

22 changes: 20 additions & 2 deletions components/calendar/src/date.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,12 +320,30 @@ impl<A: AsCalendar> Date<A> {
&self.calendar
}

#[cfg(test)]
pub(crate) fn to_fixed(&self) -> calendrical_calculations::rata_die::RataDie {
#[doc(hidden)]
pub fn to_fixed(&self) -> calendrical_calculations::rata_die::RataDie {
Iso::fixed_from_iso(self.to_iso().inner)
}
}

impl Date<Iso> {
/// Calculates the number of days between two dates.
///
/// ```rust
/// use icu::calendar::Date;
/// use icu::calendar::DateDurationUnit;
///
/// let a = Date::try_new_iso(1994, 12, 10).unwrap();
/// let b = Date::try_new_iso(2024, 10, 30).unwrap();
///
/// assert_eq!(b.days_since(a), 10_917);
/// ```
#[doc(hidden)] // unstable
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I would prefer embracing the replacement APIs but we can do that later

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is already a hidden until API which is more powerful than this, so I don't want to stabilise this. until is not fully implemented, I'd have used it if the max-unit argument were respected.

pub fn days_since(&self, other: Date<Iso>) -> i32 {
(Iso::fixed_from_iso(*self.inner()) - Iso::fixed_from_iso(other.inner)) as i32
}
}

impl<C: IntoAnyCalendar, A: AsCalendar<Calendar = C>> Date<A> {
/// Type-erase the date, converting it to a date for [`AnyCalendar`]
pub fn to_any(&self) -> Date<AnyCalendar> {
Expand Down
101 changes: 1 addition & 100 deletions components/calendar/src/iso.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
use crate::calendar_arithmetic::{ArithmeticDate, CalendarArithmetic};
use crate::error::DateError;
use crate::{types, Calendar, Date, DateDuration, DateDurationUnit, DateTime, RangeError, Time};
use calendrical_calculations::helpers::{i64_to_saturated_i32, I32CastError};
use calendrical_calculations::helpers::I32CastError;
use calendrical_calculations::rata_die::RataDie;
use tinystr::tinystr;

Expand Down Expand Up @@ -252,11 +252,6 @@ impl Date<Iso> {
.map(IsoDateInner)
.map(|inner| Date::from_raw(inner, Iso))
}

/// Constructs an ISO date representing the UNIX epoch on January 1, 1970.
pub fn unix_epoch() -> Self {
Date::from_raw(IsoDateInner(ArithmeticDate::new_unchecked(1970, 1, 1)), Iso)
}
}

impl DateTime<Iso> {
Expand Down Expand Up @@ -288,83 +283,6 @@ impl DateTime<Iso> {
time: Time::try_new(hour, minute, second, 0)?,
})
}

/// Constructs an ISO datetime representing the UNIX epoch on January 1, 1970
/// at midnight.
pub fn local_unix_epoch() -> Self {
DateTime {
date: Date::unix_epoch(),
time: Time::midnight(),
}
}

/// Minute count representation of calendars starting from 00:00:00 on Jan 1st, 1970.
///
/// ```rust
/// use icu::calendar::DateTime;
///
/// let today = DateTime::try_new_iso(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),
/// today
/// );
///
/// let today = DateTime::try_new_iso(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), 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;
let unix_epoch = Iso::fixed_from_iso(Date::unix_epoch().inner);
let result = (Iso::fixed_from_iso(*self.date.inner()) - unix_epoch) * minutes_a_day
+ i64::from(self.time.hour.number()) * minutes_a_hour
+ i64::from(self.time.minute.number());
i64_to_saturated_i32(result)
}

/// Convert minute count since 00:00:00 on Jan 1st, 1970 to ISO Date.
///
/// # Examples
///
/// ```rust
/// use icu::calendar::DateTime;
///
/// // After Unix Epoch
/// let today = DateTime::try_new_iso(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),
/// today
/// );
///
/// // Unix Epoch
/// let today = DateTime::try_new_iso(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), today);
///
/// // Before Unix Epoch
/// let today = DateTime::try_new_iso(1967, 4, 6, 20, 40, 0).unwrap();
///
/// assert_eq!(today.minutes_since_local_unix_epoch(), -1440200);
/// assert_eq!(
/// DateTime::from_minutes_since_local_unix_epoch(-1440200),
/// today
/// );
/// ```
pub fn from_minutes_since_local_unix_epoch(minute: i32) -> DateTime<Iso> {
let (time, extra_days) = Time::from_minute_with_remainder_days(minute);
let unix_epoch = Date::unix_epoch();
let unix_epoch_days = Iso::fixed_from_iso(unix_epoch.inner);
let date = Iso::iso_from_fixed(unix_epoch_days + extra_days as i64);
DateTime { date, time }
}
}

impl Iso {
Expand Down Expand Up @@ -823,21 +741,4 @@ mod test {
check(1461, 4, 12, 31); // leap year
check(1462, 5, 1, 1);
}

#[test]
fn test_from_minutes_since_local_unix_epoch() {
fn check(minutes: i32, year: i32, month: u8, day: u8, hour: u8, minute: u8) {
let today = DateTime::try_new_iso(year, month, day, hour, minute, 0).unwrap();
assert_eq!(today.minutes_since_local_unix_epoch(), minutes);
assert_eq!(
DateTime::from_minutes_since_local_unix_epoch(minutes),
today
);
}

check(-1441, 1969, 12, 30, 23, 59);
check(-1440, 1969, 12, 31, 0, 0);
check(-1439, 1969, 12, 31, 0, 1);
check(-2879, 1969, 12, 30, 0, 1);
}
}
147 changes: 0 additions & 147 deletions components/calendar/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -679,153 +679,6 @@ impl Time {
nanosecond: nanosecond.try_into()?,
})
}

/// Takes a number of minutes, which could be positive or negative, and returns the Time
/// and the day number, which could be positive or negative.
pub(crate) fn from_minute_with_remainder_days(minute: i32) -> (Time, i32) {
let (extra_days, minute_in_day) = (minute.div_euclid(1440), minute.rem_euclid(1440));
let (hours, minutes) = (minute_in_day / 60, minute_in_day % 60);
#[allow(clippy::unwrap_used)] // values are moduloed to be in range
(
Self {
hour: (hours as u8).try_into().unwrap(),
minute: (minutes as u8).try_into().unwrap(),
second: IsoSecond::zero(),
nanosecond: NanoSecond::zero(),
},
extra_days,
)
}
}

#[test]
fn test_from_minute_with_remainder_days() {
#[derive(Debug)]
struct TestCase {
minute: i32,
expected_time: Time,
expected_remainder: i32,
}
let zero_time = Time::new(
IsoHour::zero(),
IsoMinute::zero(),
IsoSecond::zero(),
NanoSecond::zero(),
);
let first_minute_in_day = Time::new(
IsoHour::zero(),
IsoMinute::try_from(1u8).unwrap(),
IsoSecond::zero(),
NanoSecond::zero(),
);
let last_minute_in_day = Time::new(
IsoHour::try_from(23u8).unwrap(),
IsoMinute::try_from(59u8).unwrap(),
IsoSecond::zero(),
NanoSecond::zero(),
);
let cases = [
TestCase {
minute: 0,
expected_time: zero_time,
expected_remainder: 0,
},
TestCase {
minute: 30,
expected_time: Time::new(
IsoHour::zero(),
IsoMinute::try_from(30u8).unwrap(),
IsoSecond::zero(),
NanoSecond::zero(),
),
expected_remainder: 0,
},
TestCase {
minute: 60,
expected_time: Time::new(
IsoHour::try_from(1u8).unwrap(),
IsoMinute::zero(),
IsoSecond::zero(),
NanoSecond::zero(),
),
expected_remainder: 0,
},
TestCase {
minute: 90,
expected_time: Time::new(
IsoHour::try_from(1u8).unwrap(),
IsoMinute::try_from(30u8).unwrap(),
IsoSecond::zero(),
NanoSecond::zero(),
),
expected_remainder: 0,
},
TestCase {
minute: 1439,
expected_time: last_minute_in_day,
expected_remainder: 0,
},
TestCase {
minute: 1440,
expected_time: Time::new(
IsoHour::zero(),
IsoMinute::zero(),
IsoSecond::zero(),
NanoSecond::zero(),
),
expected_remainder: 1,
},
TestCase {
minute: 1441,
expected_time: first_minute_in_day,
expected_remainder: 1,
},
TestCase {
minute: i32::MAX,
expected_time: Time::new(
IsoHour::try_from(2u8).unwrap(),
IsoMinute::try_from(7u8).unwrap(),
IsoSecond::zero(),
NanoSecond::zero(),
),
expected_remainder: 1491308,
},
TestCase {
minute: -1,
expected_time: last_minute_in_day,
expected_remainder: -1,
},
TestCase {
minute: -1439,
expected_time: first_minute_in_day,
expected_remainder: -1,
},
TestCase {
minute: -1440,
expected_time: zero_time,
expected_remainder: -1,
},
TestCase {
minute: -1441,
expected_time: last_minute_in_day,
expected_remainder: -2,
},
TestCase {
minute: i32::MIN,
expected_time: Time::new(
IsoHour::try_from(21u8).unwrap(),
IsoMinute::try_from(52u8).unwrap(),
IsoSecond::zero(),
NanoSecond::zero(),
),
expected_remainder: -1491309,
},
];
for cas in cases {
let (actual_time, actual_remainder) = Time::from_minute_with_remainder_days(cas.minute);
assert_eq!(actual_time, cas.expected_time, "{cas:?}");
assert_eq!(actual_remainder, cas.expected_remainder, "{cas:?}");
}
}

/// A weekday in a 7-day week, according to ISO-8601.
Expand Down
5 changes: 4 additions & 1 deletion components/datetime/benches/datetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ fn datetime_benches(c: &mut Criterion) {
time,
// zone is unused but we need to make the types match
zone: TimeZoneInfo::utc()
.at_time((Date::unix_epoch(), Time::midnight()))
.at_time((
Date::try_new_iso(2024, 1, 1).unwrap(),
Time::midnight(),
))
.with_zone_variant(ZoneVariant::standard()),
}
}
Expand Down
5 changes: 2 additions & 3 deletions components/datetime/src/neo_pattern.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ size_test!(DateTimePattern, date_time_pattern_size, 32);
/// then check the resolved components:
///
/// ```
/// use icu::calendar::DateTime;
/// use icu::calendar::Date;
/// use icu::calendar::Gregorian;
/// use icu::datetime::fieldset::YMD;
/// use icu::datetime::neo_pattern::DateTimePattern;
Expand All @@ -59,8 +59,7 @@ size_test!(DateTimePattern, date_time_pattern_size, 32);
/// )
/// .unwrap()
/// // The pattern can depend on the datetime being formatted.
/// // For this example, we'll choose the local Unix epoch.
/// .format(&DateTime::local_unix_epoch().to_calendar(Gregorian))
/// .format(&Date::try_new_iso(2024, 1, 1).unwrap().to_calendar(Gregorian))
/// .pattern();
/// assert_writeable_eq!(data_pattern, pattern_str);
/// assert_eq!(custom_pattern, data_pattern);
Expand Down
2 changes: 1 addition & 1 deletion components/datetime/src/provider/time_zones.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ impl<'a> zerovec::maps::ZeroMapKV<'a> for MetazoneId {
#[yoke(prove_covariance_manually)]
pub struct MetazonePeriodV1<'data>(
/// 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.
/// the number of minutes since the local [`EPOCH`](icu_timezone::provider::EPOCH). It represents when the metazone started to be used.
#[cfg_attr(feature = "serde", serde(borrow))]
pub ZeroMap2d<'data, TimeZoneBcp47Id, IsoMinutesSinceEpoch, Option<MetazoneId>>,
);
Loading