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

Implement operations for core::time::Duration on DateTime types #1229

Merged
merged 3 commits into from
Aug 29, 2023
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
43 changes: 41 additions & 2 deletions src/datetime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use core::cmp::Ordering;
use core::fmt::Write;
use core::ops::{Add, AddAssign, Sub, SubAssign};
use core::time::Duration;
use core::{fmt, hash, str};
#[cfg(feature = "std")]
use std::time::{SystemTime, UNIX_EPOCH};
Expand Down Expand Up @@ -1131,6 +1132,17 @@
}
}

impl<Tz: TimeZone> Add<Duration> for DateTime<Tz> {
type Output = DateTime<Tz>;

#[inline]
fn add(self, rhs: Duration) -> DateTime<Tz> {
let rhs = OldDuration::from_std(rhs)
.expect("overflow converting from core::time::Duration to chrono::Duration");
self.checked_add_signed(rhs).expect("`DateTime + Duration` overflowed")
}
}

impl<Tz: TimeZone> AddAssign<OldDuration> for DateTime<Tz> {
#[inline]
fn add_assign(&mut self, rhs: OldDuration) {
Expand All @@ -1141,6 +1153,15 @@
}
}

impl<Tz: TimeZone> AddAssign<Duration> for DateTime<Tz> {
#[inline]
fn add_assign(&mut self, rhs: Duration) {
let rhs = OldDuration::from_std(rhs)
.expect("overflow converting from core::time::Duration to chrono::Duration");
*self += rhs;
}
}

impl<Tz: TimeZone> Add<Months> for DateTime<Tz> {
type Output = DateTime<Tz>;

Expand All @@ -1158,6 +1179,17 @@
}
}

impl<Tz: TimeZone> Sub<Duration> for DateTime<Tz> {
type Output = DateTime<Tz>;

#[inline]
fn sub(self, rhs: Duration) -> DateTime<Tz> {
let rhs = OldDuration::from_std(rhs)
.expect("overflow converting from core::time::Duration to chrono::Duration");
self.checked_sub_signed(rhs).expect("`DateTime - Duration` overflowed")
}

Check warning on line 1190 in src/datetime/mod.rs

View check run for this annotation

Codecov / codecov/patch

src/datetime/mod.rs#L1186-L1190

Added lines #L1186 - L1190 were not covered by tests
}

impl<Tz: TimeZone> SubAssign<OldDuration> for DateTime<Tz> {
#[inline]
fn sub_assign(&mut self, rhs: OldDuration) {
Expand All @@ -1168,6 +1200,15 @@
}
}

impl<Tz: TimeZone> SubAssign<Duration> for DateTime<Tz> {
#[inline]
fn sub_assign(&mut self, rhs: Duration) {
let rhs = OldDuration::from_std(rhs)
.expect("overflow converting from core::time::Duration to chrono::Duration");
*self -= rhs;
}

Check warning on line 1209 in src/datetime/mod.rs

View check run for this annotation

Codecov / codecov/patch

src/datetime/mod.rs#L1205-L1209

Added lines #L1205 - L1209 were not covered by tests
}

impl<Tz: TimeZone> Sub<Months> for DateTime<Tz> {
type Output = DateTime<Tz>;

Expand Down Expand Up @@ -1305,8 +1346,6 @@
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
impl<Tz: TimeZone> From<DateTime<Tz>> for SystemTime {
fn from(dt: DateTime<Tz>) -> SystemTime {
use std::time::Duration;

let sec = dt.timestamp();
let nsec = dt.timestamp_subsec_nanos();
if sec < 0 {
Expand Down
69 changes: 45 additions & 24 deletions src/datetime/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::naive::{NaiveDate, NaiveTime};
use crate::offset::{FixedOffset, TimeZone, Utc};
#[cfg(feature = "clock")]
use crate::offset::{Local, Offset};
use crate::oldtime::Duration;
use crate::oldtime::Duration as OldDuration;
use crate::{Datelike, Days, LocalResult, Months, NaiveDateTime};

#[derive(Clone)]
Expand Down Expand Up @@ -54,7 +54,7 @@ impl TimeZone for DstTester {
DstTester::TO_WINTER_MONTH_DAY.1,
)
.unwrap()
.and_time(DstTester::transition_start_local() - Duration::hours(1));
.and_time(DstTester::transition_start_local() - OldDuration::hours(1));

let local_to_summer_transition_start = NaiveDate::from_ymd_opt(
local.year(),
Expand All @@ -70,7 +70,7 @@ impl TimeZone for DstTester {
DstTester::TO_SUMMER_MONTH_DAY.1,
)
.unwrap()
.and_time(DstTester::transition_start_local() + Duration::hours(1));
.and_time(DstTester::transition_start_local() + OldDuration::hours(1));

if *local < local_to_winter_transition_end || *local >= local_to_summer_transition_end {
LocalResult::Single(DstTester::summer_offset())
Expand Down Expand Up @@ -267,7 +267,7 @@ fn ymdhms_milli(
fixedoffset
.with_ymd_and_hms(year, month, day, hour, min, sec)
.unwrap()
.checked_add_signed(Duration::milliseconds(milli))
.checked_add_signed(OldDuration::milliseconds(milli))
.unwrap()
}

Expand All @@ -287,7 +287,7 @@ fn ymdhms_micro(
fixedoffset
.with_ymd_and_hms(year, month, day, hour, min, sec)
.unwrap()
.checked_add_signed(Duration::microseconds(micro))
.checked_add_signed(OldDuration::microseconds(micro))
.unwrap()
}

Expand All @@ -307,7 +307,7 @@ fn ymdhms_nano(
fixedoffset
.with_ymd_and_hms(year, month, day, hour, min, sec)
.unwrap()
.checked_add_signed(Duration::nanoseconds(nano))
.checked_add_signed(OldDuration::nanoseconds(nano))
.unwrap()
}

Expand All @@ -328,7 +328,7 @@ fn ymdhms_milli_utc(
) -> DateTime<Utc> {
Utc.with_ymd_and_hms(year, month, day, hour, min, sec)
.unwrap()
.checked_add_signed(Duration::milliseconds(milli))
.checked_add_signed(OldDuration::milliseconds(milli))
.unwrap()
}

Expand Down Expand Up @@ -392,12 +392,12 @@ fn test_datetime_offset() {
let dt = Utc.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap();
assert_eq!(dt, edt.with_ymd_and_hms(2014, 5, 6, 3, 8, 9).unwrap());
assert_eq!(
dt + Duration::seconds(3600 + 60 + 1),
dt + OldDuration::seconds(3600 + 60 + 1),
Utc.with_ymd_and_hms(2014, 5, 6, 8, 9, 10).unwrap()
);
assert_eq!(
dt.signed_duration_since(edt.with_ymd_and_hms(2014, 5, 6, 10, 11, 12).unwrap()),
Duration::seconds(-7 * 3600 - 3 * 60 - 3)
OldDuration::seconds(-7 * 3600 - 3 * 60 - 3)
);

assert_eq!(*Utc.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap().offset(), Utc);
Expand Down Expand Up @@ -1275,16 +1275,16 @@ fn test_years_elapsed() {

// This is always at least one year because 1 year = 52.1775 weeks.
let one_year_ago =
Utc::now().date_naive() - Duration::weeks((WEEKS_PER_YEAR * 1.5).ceil() as i64);
Utc::now().date_naive() - OldDuration::weeks((WEEKS_PER_YEAR * 1.5).ceil() as i64);
// A bit more than 2 years.
let two_year_ago =
Utc::now().date_naive() - Duration::weeks((WEEKS_PER_YEAR * 2.5).ceil() as i64);
Utc::now().date_naive() - OldDuration::weeks((WEEKS_PER_YEAR * 2.5).ceil() as i64);

assert_eq!(Utc::now().date_naive().years_since(one_year_ago), Some(1));
assert_eq!(Utc::now().date_naive().years_since(two_year_ago), Some(2));

// If the given DateTime is later than now, the function will always return 0.
let future = Utc::now().date_naive() + Duration::weeks(12);
let future = Utc::now().date_naive() + OldDuration::weeks(12);
assert_eq!(Utc::now().date_naive().years_since(future), None);
}

Expand All @@ -1294,20 +1294,20 @@ fn test_datetime_add_assign() {
let datetime = naivedatetime.and_utc();
let mut datetime_add = datetime;

datetime_add += Duration::seconds(60);
assert_eq!(datetime_add, datetime + Duration::seconds(60));
datetime_add += OldDuration::seconds(60);
assert_eq!(datetime_add, datetime + OldDuration::seconds(60));

let timezone = FixedOffset::east_opt(60 * 60).unwrap();
let datetime = datetime.with_timezone(&timezone);
let datetime_add = datetime_add.with_timezone(&timezone);

assert_eq!(datetime_add, datetime + Duration::seconds(60));
assert_eq!(datetime_add, datetime + OldDuration::seconds(60));

let timezone = FixedOffset::west_opt(2 * 60 * 60).unwrap();
let datetime = datetime.with_timezone(&timezone);
let datetime_add = datetime_add.with_timezone(&timezone);

assert_eq!(datetime_add, datetime + Duration::seconds(60));
assert_eq!(datetime_add, datetime + OldDuration::seconds(60));
}

#[test]
Expand All @@ -1320,8 +1320,8 @@ fn test_datetime_add_assign_local() {

// ensure we cross a DST transition
for i in 1..=365 {
datetime_add += Duration::days(1);
assert_eq!(datetime_add, datetime + Duration::days(i))
datetime_add += OldDuration::days(1);
assert_eq!(datetime_add, datetime + OldDuration::days(i))
}
}

Expand All @@ -1331,20 +1331,20 @@ fn test_datetime_sub_assign() {
let datetime = naivedatetime.and_utc();
let mut datetime_sub = datetime;

datetime_sub -= Duration::minutes(90);
assert_eq!(datetime_sub, datetime - Duration::minutes(90));
datetime_sub -= OldDuration::minutes(90);
assert_eq!(datetime_sub, datetime - OldDuration::minutes(90));

let timezone = FixedOffset::east_opt(60 * 60).unwrap();
let datetime = datetime.with_timezone(&timezone);
let datetime_sub = datetime_sub.with_timezone(&timezone);

assert_eq!(datetime_sub, datetime - Duration::minutes(90));
assert_eq!(datetime_sub, datetime - OldDuration::minutes(90));

let timezone = FixedOffset::west_opt(2 * 60 * 60).unwrap();
let datetime = datetime.with_timezone(&timezone);
let datetime_sub = datetime_sub.with_timezone(&timezone);

assert_eq!(datetime_sub, datetime - Duration::minutes(90));
assert_eq!(datetime_sub, datetime - OldDuration::minutes(90));
}

#[test]
Expand All @@ -1357,11 +1357,32 @@ fn test_datetime_sub_assign_local() {

// ensure we cross a DST transition
for i in 1..=365 {
datetime_sub -= Duration::days(1);
assert_eq!(datetime_sub, datetime - Duration::days(i))
datetime_sub -= OldDuration::days(1);
assert_eq!(datetime_sub, datetime - OldDuration::days(i))
}
}

#[test]
fn test_core_duration_ops() {
use core::time::Duration;

let mut utc_dt = Utc.with_ymd_and_hms(2023, 8, 29, 11, 34, 12).unwrap();
let same = utc_dt + Duration::ZERO;
assert_eq!(utc_dt, same);

utc_dt += Duration::new(3600, 0);
assert_eq!(utc_dt, Utc.with_ymd_and_hms(2023, 8, 29, 12, 34, 12).unwrap());
}

#[test]
#[should_panic]
fn test_core_duration_max() {
use core::time::Duration;

let mut utc_dt = Utc.with_ymd_and_hms(2023, 8, 29, 11, 34, 12).unwrap();
utc_dt += Duration::MAX;
}

#[test]
#[cfg(all(target_os = "windows", feature = "clock"))]
fn test_from_naive_date_time_windows() {
Expand Down
37 changes: 37 additions & 0 deletions src/naive/datetime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use core::borrow::Borrow;
use core::fmt::Write;
use core::ops::{Add, AddAssign, Sub, SubAssign};
use core::time::Duration;
use core::{fmt, str};

#[cfg(feature = "rkyv")]
Expand Down Expand Up @@ -1516,13 +1517,31 @@
}
}

impl Add<Duration> for NaiveDateTime {
type Output = NaiveDateTime;

#[inline]
fn add(self, rhs: Duration) -> NaiveDateTime {
let rhs = OldDuration::from_std(rhs)
.expect("overflow converting from core::time::Duration to chrono::Duration");
self.checked_add_signed(rhs).expect("`NaiveDateTime + Duration` overflowed")
}
}

impl AddAssign<OldDuration> for NaiveDateTime {
#[inline]
fn add_assign(&mut self, rhs: OldDuration) {
*self = self.add(rhs);
}
}

impl AddAssign<Duration> for NaiveDateTime {
#[inline]
fn add_assign(&mut self, rhs: Duration) {
*self = self.add(rhs);
}
}

impl Add<Months> for NaiveDateTime {
type Output = NaiveDateTime;

Expand Down Expand Up @@ -1625,13 +1644,31 @@
}
}

impl Sub<Duration> for NaiveDateTime {
type Output = NaiveDateTime;

#[inline]
fn sub(self, rhs: Duration) -> NaiveDateTime {
let rhs = OldDuration::from_std(rhs)
.expect("overflow converting from core::time::Duration to chrono::Duration");
self.checked_sub_signed(rhs).expect("`NaiveDateTime - Duration` overflowed")
}

Check warning on line 1655 in src/naive/datetime/mod.rs

View check run for this annotation

Codecov / codecov/patch

src/naive/datetime/mod.rs#L1651-L1655

Added lines #L1651 - L1655 were not covered by tests
}

impl SubAssign<OldDuration> for NaiveDateTime {
#[inline]
fn sub_assign(&mut self, rhs: OldDuration) {
*self = self.sub(rhs);
}
}

impl SubAssign<Duration> for NaiveDateTime {
#[inline]
fn sub_assign(&mut self, rhs: Duration) {
*self = self.sub(rhs);
}

Check warning on line 1669 in src/naive/datetime/mod.rs

View check run for this annotation

Codecov / codecov/patch

src/naive/datetime/mod.rs#L1667-L1669

Added lines #L1667 - L1669 were not covered by tests
}

/// A subtraction of Months from `NaiveDateTime` clamped to valid days in resulting month.
///
/// # Panics
Expand Down