Skip to content

Commit

Permalink
Add overflowing_naive_local and NaiveDateTime::unchecked_add_offset
Browse files Browse the repository at this point in the history
  • Loading branch information
pitdicker committed Jun 8, 2023
1 parent 9e0d4b4 commit 7f89920
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 1 deletion.
23 changes: 22 additions & 1 deletion src/datetime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,14 @@ impl<Tz: TimeZone> DateTime<Tz> {
///
/// # Example
///
/// # Panics
///
/// [`DateTime`] internally stores the date and time in UTC with a [`NaiveDateTime`]. This
/// method will panic if the offset from UTC would push the local datetime outside of the
/// representable range of a [`DateTime`].
///
/// # Example
///
/// ```
/// use chrono::prelude::*;
///
Expand Down Expand Up @@ -463,7 +471,20 @@ impl<Tz: TimeZone> DateTime<Tz> {
#[inline]
#[must_use]
pub fn naive_local(&self) -> NaiveDateTime {
self.datetime + self.offset.fix()
self.datetime
.checked_add_offset(self.offset.fix())
.expect("Local time out of range for `NaiveDateTime`")
}

/// Returns a view to the naive local datetime.
///
/// This makes use of the buffer space outside of the representable range of values of
/// `NaiveDateTime`. The result can be used as intermediate value, but should never be exposed
/// outside chrono.
#[inline]
#[must_use]
pub(crate) fn overflowing_naive_local(&self) -> NaiveDateTime {
self.datetime.unchecked_add_offset(self.offset.fix())
}

/// Retrieve the elapsed years from now to the given [`DateTime`].
Expand Down
12 changes: 12 additions & 0 deletions src/naive/datetime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,18 @@ impl NaiveDateTime {
self.date.add_days(days, false).map(|date| NaiveDateTime { date, time })
}

/// Adds given `FixedOffset` to the current datetime.
/// The resulting value may be outside the valid range of [`NaiveDateTime`].
///
/// This can be useful for intermediate values, but the resulting out-of-range `NaiveDate`
/// should not be exposed to library users.
#[must_use]
pub(crate) fn unchecked_add_offset(self, rhs: FixedOffset) -> NaiveDateTime {
let (time, days) = self.time.overflowing_add_offset(rhs);
let date = self.date.add_days(days, true).unwrap();
NaiveDateTime { date, time }
}

/// Subtracts given `Duration` from the current date and time.
///
/// As a part of Chrono's [leap second handling](./struct.NaiveTime.html#leap-second-handling),
Expand Down
26 changes: 26 additions & 0 deletions src/naive/datetime/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -441,3 +441,29 @@ fn test_and_timezone_min_max_dates() {
}
}
}

#[test]
fn test_unchecked_add_offset() {
let ymdhmsm = |y, m, d, h, mn, s, mi| {
NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_milli_opt(h, mn, s, mi).unwrap()
};
let positive_offset = FixedOffset::east_opt(2 * 60 * 60).unwrap();
// regular date
let dt = ymdhmsm(2023, 5, 5, 20, 10, 0, 0);
assert_eq!(dt.unchecked_add_offset(positive_offset), ymdhmsm(2023, 5, 5, 22, 10, 0, 0));
// leap second is preserved
let dt = ymdhmsm(2023, 6, 30, 23, 59, 59, 1_000);
assert_eq!(dt.unchecked_add_offset(positive_offset), ymdhmsm(2023, 7, 1, 1, 59, 59, 1_000));
// out of range
assert!(NaiveDateTime::MAX.unchecked_add_offset(positive_offset) > NaiveDateTime::MAX);

let negative_offset = FixedOffset::west_opt(2 * 60 * 60).unwrap();
// regular date
let dt = ymdhmsm(2023, 5, 5, 20, 10, 0, 0);
assert_eq!(dt.unchecked_add_offset(negative_offset), ymdhmsm(2023, 5, 5, 18, 10, 0, 0));
// leap second is preserved
let dt = ymdhmsm(2023, 6, 30, 23, 59, 59, 1_000);
assert_eq!(dt.unchecked_add_offset(negative_offset), ymdhmsm(2023, 6, 30, 21, 59, 59, 1_000));
// out of range
assert!(NaiveDateTime::MIN.unchecked_add_offset(negative_offset) < NaiveDateTime::MIN);
}

0 comments on commit 7f89920

Please sign in to comment.