Documentation and complete rewrite for `NaiveDateTime` operations.
- `NaiveDateTime` is now almost completely annotated with examples. - Introduced `NaiveTime::overflowing_{add,sub}` for the correct handling of overflow/underflow of `NaiveTime`. - `NaiveDateTime +/- Duration` operation is rewritten with those methods, eliminating any problem against leap seconds. (Thus this is yet another slight breaking change, but considered a bug fix.)
This commit is contained in:
parent
8b382fca45
commit
582f1166f2
|
@ -86,6 +86,9 @@ const MIN_DAYS_FROM_YEAR_0: i32 = (MIN_YEAR + 400_000) * 365 +
|
|||
(MIN_YEAR + 400_000) / 100 +
|
||||
(MIN_YEAR + 400_000) / 400 - 146097_000;
|
||||
|
||||
#[cfg(test)] // only used for testing, but duplicated in naive::datetime
|
||||
const MAX_BITS: usize = 44;
|
||||
|
||||
/// ISO 8601 calendar date without timezone.
|
||||
/// Allows for every [proleptic Gregorian date](./index.html#calendar-date)
|
||||
/// from Jan 1, 262145 BCE to Dec 31, 262143 CE.
|
||||
|
@ -109,6 +112,13 @@ fn test_date_bounds() {
|
|||
"`MIN` should have a year flag {:?}", calculated_min.of().flags());
|
||||
assert!(MAX == calculated_max,
|
||||
"`MAX` should have a year flag {:?}", calculated_max.of().flags());
|
||||
|
||||
// let's also check that the entire range do not exceed 2^44 seconds
|
||||
// (sometimes used for bounding `Duration` against overflow)
|
||||
let maxsecs = (MAX - MIN).num_seconds();
|
||||
let maxsecs = maxsecs + 86401; // also take care of DateTime
|
||||
assert!(maxsecs < (1 << MAX_BITS),
|
||||
"The entire `NaiveDate` range somehow exceeds 2^{} seconds", MAX_BITS);
|
||||
}
|
||||
|
||||
impl NaiveDate {
|
||||
|
@ -795,8 +805,8 @@ impl NaiveDate {
|
|||
/// let d = NaiveDate::from_ymd(2015, 9, 5);
|
||||
/// assert_eq!(d.checked_add(Duration::days(40)), Some(NaiveDate::from_ymd(2015, 10, 15)));
|
||||
/// assert_eq!(d.checked_add(Duration::days(-40)), Some(NaiveDate::from_ymd(2015, 7, 27)));
|
||||
/// assert_eq!(d.checked_add(Duration::days(1000_000_000)), None);
|
||||
/// assert_eq!(d.checked_add(Duration::days(-1000_000_000)), None);
|
||||
/// assert_eq!(d.checked_add(Duration::days(1_000_000_000)), None);
|
||||
/// assert_eq!(d.checked_add(Duration::days(-1_000_000_000)), None);
|
||||
/// assert_eq!(MAX.checked_add(Duration::days(1)), None);
|
||||
/// ~~~~
|
||||
pub fn checked_add(self, rhs: Duration) -> Option<NaiveDate> {
|
||||
|
@ -826,8 +836,8 @@ impl NaiveDate {
|
|||
/// let d = NaiveDate::from_ymd(2015, 9, 5);
|
||||
/// assert_eq!(d.checked_sub(Duration::days(40)), Some(NaiveDate::from_ymd(2015, 7, 27)));
|
||||
/// assert_eq!(d.checked_sub(Duration::days(-40)), Some(NaiveDate::from_ymd(2015, 10, 15)));
|
||||
/// assert_eq!(d.checked_sub(Duration::days(1000_000_000)), None);
|
||||
/// assert_eq!(d.checked_sub(Duration::days(-1000_000_000)), None);
|
||||
/// assert_eq!(d.checked_sub(Duration::days(1_000_000_000)), None);
|
||||
/// assert_eq!(d.checked_sub(Duration::days(-1_000_000_000)), None);
|
||||
/// assert_eq!(MIN.checked_sub(Duration::days(1)), None);
|
||||
/// ~~~~
|
||||
pub fn checked_sub(self, rhs: Duration) -> Option<NaiveDate> {
|
||||
|
|
|
@ -16,6 +16,14 @@ use naive::date::NaiveDate;
|
|||
use format::{Item, Numeric, Pad, Fixed};
|
||||
use format::{parse, Parsed, ParseError, ParseResult, DelayedFormat, StrftimeItems};
|
||||
|
||||
/// The tight upper bound guarantees that a duration with `|Duration| >= 2^MAX_SECS_BITS`
|
||||
/// will always overflow the addition with any date and time type.
|
||||
///
|
||||
/// So why is this needed? `Duration::seconds(rhs)` may overflow, and we don't have
|
||||
/// an alternative returning `Option` or `Result`. Thus we need some early bound to avoid
|
||||
/// touching that call when we are already sure that it WILL overflow...
|
||||
const MAX_SECS_BITS: usize = 44;
|
||||
|
||||
/// ISO 8601 combined date and time without timezone.
|
||||
///
|
||||
/// # Example
|
||||
|
@ -337,45 +345,133 @@ impl NaiveDateTime {
|
|||
|
||||
/// Adds given `Duration` to the current date and time.
|
||||
///
|
||||
/// As a part of Chrono's [leap second handling](../time/index.html#leap-second-handling),
|
||||
/// the addition assumes that **there is no leap second ever**,
|
||||
/// except when the `NaiveDateTime` itself represents a leap second
|
||||
/// in which case the assumption becomes that **there is exactly a single leap second ever**.
|
||||
///
|
||||
/// Returns `None` when it will result in overflow.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::{NaiveDate, Duration};
|
||||
///
|
||||
/// let from_ymd = NaiveDate::from_ymd;
|
||||
///
|
||||
/// let d = from_ymd(2016, 7, 8);
|
||||
/// let hms = |h, m, s| d.and_hms(h, m, s);
|
||||
/// assert_eq!(hms(3, 5, 7).checked_add(Duration::zero()), Some(hms(3, 5, 7)));
|
||||
/// assert_eq!(hms(3, 5, 7).checked_add(Duration::seconds(1)), Some(hms(3, 5, 8)));
|
||||
/// assert_eq!(hms(3, 5, 7).checked_add(Duration::seconds(-1)), Some(hms(3, 5, 6)));
|
||||
/// assert_eq!(hms(3, 5, 7).checked_add(Duration::seconds(3600 + 60)), Some(hms(4, 6, 7)));
|
||||
/// assert_eq!(hms(3, 5, 7).checked_add(Duration::seconds(86400)),
|
||||
/// Some(from_ymd(2016, 7, 9).and_hms(3, 5, 7)));
|
||||
///
|
||||
/// let hmsm = |h, m, s, milli| d.and_hms_milli(h, m, s, milli);
|
||||
/// assert_eq!(hmsm(3, 5, 7, 980).checked_add(Duration::milliseconds(450)),
|
||||
/// Some(hmsm(3, 5, 8, 430)));
|
||||
/// ~~~~
|
||||
///
|
||||
/// Overflow returns `None`.
|
||||
///
|
||||
/// ~~~~
|
||||
/// # use chrono::{NaiveDate, Duration};
|
||||
/// # let hms = |h, m, s| NaiveDate::from_ymd(2016, 7, 8).and_hms(h, m, s);
|
||||
/// assert_eq!(hms(3, 5, 7).checked_add(Duration::days(1_000_000_000)), None);
|
||||
/// ~~~~
|
||||
///
|
||||
/// Leap seconds are handled,
|
||||
/// but the addition assumes that it is the only leap second happened.
|
||||
///
|
||||
/// ~~~~
|
||||
/// # use chrono::{NaiveDate, Duration};
|
||||
/// # let from_ymd = NaiveDate::from_ymd;
|
||||
/// # let hmsm = |h, m, s, milli| from_ymd(2016, 7, 8).and_hms_milli(h, m, s, milli);
|
||||
/// let leap = hmsm(3, 5, 59, 1_300);
|
||||
/// assert_eq!(leap.checked_add(Duration::zero()), Some(hmsm(3, 5, 59, 1_300)));
|
||||
/// assert_eq!(leap.checked_add(Duration::milliseconds(-500)), Some(hmsm(3, 5, 59, 800)));
|
||||
/// assert_eq!(leap.checked_add(Duration::milliseconds(500)), Some(hmsm(3, 5, 59, 1_800)));
|
||||
/// assert_eq!(leap.checked_add(Duration::milliseconds(800)), Some(hmsm(3, 6, 0, 100)));
|
||||
/// assert_eq!(leap.checked_add(Duration::seconds(10)), Some(hmsm(3, 6, 9, 300)));
|
||||
/// assert_eq!(leap.checked_add(Duration::seconds(-10)), Some(hmsm(3, 5, 50, 300)));
|
||||
/// assert_eq!(leap.checked_add(Duration::days(1)),
|
||||
/// Some(from_ymd(2016, 7, 9).and_hms_milli(3, 5, 59, 300)));
|
||||
/// ~~~~
|
||||
pub fn checked_add(self, rhs: Duration) -> Option<NaiveDateTime> {
|
||||
// Duration does not directly give its parts, so we need some additional calculations.
|
||||
let days = rhs.num_days();
|
||||
let nanos = (rhs - Duration::days(days)).num_nanoseconds().unwrap();
|
||||
debug_assert!(Duration::days(days) + Duration::nanoseconds(nanos) == rhs);
|
||||
debug_assert!(-86400_000_000_000 < nanos && nanos < 86400_000_000_000);
|
||||
let (time, rhs) = self.time.overflowing_add(rhs);
|
||||
|
||||
let mut date = try_opt!(self.date.checked_add(Duration::days(days)));
|
||||
let time = self.time + Duration::nanoseconds(nanos);
|
||||
|
||||
// time always wraps around, but date needs to be adjusted for overflow.
|
||||
if nanos < 0 && time > self.time {
|
||||
date = try_opt!(date.pred_opt());
|
||||
} else if nanos > 0 && time < self.time {
|
||||
date = try_opt!(date.succ_opt());
|
||||
// early checking to avoid overflow in Duration::seconds
|
||||
if rhs <= (-1 << MAX_SECS_BITS) || rhs >= (1 << MAX_SECS_BITS) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let date = try_opt!(self.date.checked_add(Duration::seconds(rhs)));
|
||||
Some(NaiveDateTime { date: date, time: time })
|
||||
}
|
||||
|
||||
/// Subtracts given `Duration` from the current date and time.
|
||||
///
|
||||
/// As a part of Chrono's [leap second handling](../time/index.html#leap-second-handling),
|
||||
/// the subtraction assumes that **there is no leap second ever**,
|
||||
/// except when the `NaiveDateTime` itself represents a leap second
|
||||
/// in which case the assumption becomes that **there is exactly a single leap second ever**.
|
||||
///
|
||||
/// Returns `None` when it will result in overflow.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::{NaiveDate, Duration};
|
||||
///
|
||||
/// let from_ymd = NaiveDate::from_ymd;
|
||||
///
|
||||
/// let d = from_ymd(2016, 7, 8);
|
||||
/// let hms = |h, m, s| d.and_hms(h, m, s);
|
||||
/// assert_eq!(hms(3, 5, 7).checked_sub(Duration::zero()), Some(hms(3, 5, 7)));
|
||||
/// assert_eq!(hms(3, 5, 7).checked_sub(Duration::seconds(1)), Some(hms(3, 5, 6)));
|
||||
/// assert_eq!(hms(3, 5, 7).checked_sub(Duration::seconds(-1)), Some(hms(3, 5, 8)));
|
||||
/// assert_eq!(hms(3, 5, 7).checked_sub(Duration::seconds(3600 + 60)), Some(hms(2, 4, 7)));
|
||||
/// assert_eq!(hms(3, 5, 7).checked_sub(Duration::seconds(86400)),
|
||||
/// Some(from_ymd(2016, 7, 7).and_hms(3, 5, 7)));
|
||||
///
|
||||
/// let hmsm = |h, m, s, milli| d.and_hms_milli(h, m, s, milli);
|
||||
/// assert_eq!(hmsm(3, 5, 7, 450).checked_sub(Duration::milliseconds(670)),
|
||||
/// Some(hmsm(3, 5, 6, 780)));
|
||||
/// ~~~~
|
||||
///
|
||||
/// Overflow returns `None`.
|
||||
///
|
||||
/// ~~~~
|
||||
/// # use chrono::{NaiveDate, Duration};
|
||||
/// # let hms = |h, m, s| NaiveDate::from_ymd(2016, 7, 8).and_hms(h, m, s);
|
||||
/// assert_eq!(hms(3, 5, 7).checked_sub(Duration::days(1_000_000_000)), None);
|
||||
/// ~~~~
|
||||
///
|
||||
/// Leap seconds are handled,
|
||||
/// but the subtraction assumes that it is the only leap second happened.
|
||||
///
|
||||
/// ~~~~
|
||||
/// # use chrono::{NaiveDate, Duration};
|
||||
/// # let from_ymd = NaiveDate::from_ymd;
|
||||
/// # let hmsm = |h, m, s, milli| from_ymd(2016, 7, 8).and_hms_milli(h, m, s, milli);
|
||||
/// let leap = hmsm(3, 5, 59, 1_300);
|
||||
/// assert_eq!(leap.checked_sub(Duration::zero()), Some(hmsm(3, 5, 59, 1_300)));
|
||||
/// assert_eq!(leap.checked_sub(Duration::milliseconds(200)), Some(hmsm(3, 5, 59, 1_100)));
|
||||
/// assert_eq!(leap.checked_sub(Duration::milliseconds(500)), Some(hmsm(3, 5, 59, 800)));
|
||||
/// assert_eq!(leap.checked_sub(Duration::seconds(60)), Some(hmsm(3, 5, 0, 300)));
|
||||
/// assert_eq!(leap.checked_sub(Duration::days(1)),
|
||||
/// Some(from_ymd(2016, 7, 7).and_hms_milli(3, 6, 0, 300)));
|
||||
/// ~~~~
|
||||
pub fn checked_sub(self, rhs: Duration) -> Option<NaiveDateTime> {
|
||||
// Duration does not directly give its parts, so we need some additional calculations.
|
||||
let days = rhs.num_days();
|
||||
let nanos = (rhs - Duration::days(days)).num_nanoseconds().unwrap();
|
||||
debug_assert!(Duration::days(days) + Duration::nanoseconds(nanos) == rhs);
|
||||
debug_assert!(-86400_000_000_000 < nanos && nanos < 86400_000_000_000);
|
||||
let (time, rhs) = self.time.overflowing_sub(rhs);
|
||||
|
||||
let mut date = try_opt!(self.date.checked_sub(Duration::days(days)));
|
||||
let time = self.time - Duration::nanoseconds(nanos);
|
||||
|
||||
// time always wraps around, but date needs to be adjusted for overflow.
|
||||
if nanos > 0 && time > self.time {
|
||||
date = try_opt!(date.pred_opt());
|
||||
} else if nanos < 0 && time < self.time {
|
||||
date = try_opt!(date.succ_opt());
|
||||
// early checking to avoid overflow in Duration::seconds
|
||||
if rhs <= (-1 << MAX_SECS_BITS) || rhs >= (1 << MAX_SECS_BITS) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let date = try_opt!(self.date.checked_sub(Duration::seconds(rhs)));
|
||||
Some(NaiveDateTime { date: date, time: time })
|
||||
}
|
||||
|
||||
|
@ -947,6 +1043,55 @@ impl hash::Hash for NaiveDateTime {
|
|||
}
|
||||
}
|
||||
|
||||
/// An addition of `Duration` to `NaiveDateTime` yields another `NaiveDateTime`.
|
||||
///
|
||||
/// As a part of Chrono's [leap second handling](../time/index.html#leap-second-handling),
|
||||
/// the addition assumes that **there is no leap second ever**,
|
||||
/// except when the `NaiveDateTime` itself represents a leap second
|
||||
/// in which case the assumption becomes that **there is exactly a single leap second ever**.
|
||||
///
|
||||
/// Panics on underflow or overflow.
|
||||
/// Use [`NaiveDateTime::checked_add`](#method.checked_add) to detect that.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::{NaiveDate, Duration};
|
||||
///
|
||||
/// let from_ymd = NaiveDate::from_ymd;
|
||||
///
|
||||
/// let d = from_ymd(2016, 7, 8);
|
||||
/// let hms = |h, m, s| d.and_hms(h, m, s);
|
||||
/// assert_eq!(hms(3, 5, 7) + Duration::zero(), hms(3, 5, 7));
|
||||
/// assert_eq!(hms(3, 5, 7) + Duration::seconds(1), hms(3, 5, 8));
|
||||
/// assert_eq!(hms(3, 5, 7) + Duration::seconds(-1), hms(3, 5, 6));
|
||||
/// assert_eq!(hms(3, 5, 7) + Duration::seconds(3600 + 60), hms(4, 6, 7));
|
||||
/// assert_eq!(hms(3, 5, 7) + Duration::seconds(86400),
|
||||
/// from_ymd(2016, 7, 9).and_hms(3, 5, 7));
|
||||
/// assert_eq!(hms(3, 5, 7) + Duration::days(365),
|
||||
/// from_ymd(2017, 7, 8).and_hms(3, 5, 7));
|
||||
///
|
||||
/// let hmsm = |h, m, s, milli| d.and_hms_milli(h, m, s, milli);
|
||||
/// assert_eq!(hmsm(3, 5, 7, 980) + Duration::milliseconds(450), hmsm(3, 5, 8, 430));
|
||||
/// ~~~~
|
||||
///
|
||||
/// Leap seconds are handled,
|
||||
/// but the addition assumes that it is the only leap second happened.
|
||||
///
|
||||
/// ~~~~
|
||||
/// # use chrono::{NaiveDate, Duration};
|
||||
/// # let from_ymd = NaiveDate::from_ymd;
|
||||
/// # let hmsm = |h, m, s, milli| from_ymd(2016, 7, 8).and_hms_milli(h, m, s, milli);
|
||||
/// let leap = hmsm(3, 5, 59, 1_300);
|
||||
/// assert_eq!(leap + Duration::zero(), hmsm(3, 5, 59, 1_300));
|
||||
/// assert_eq!(leap + Duration::milliseconds(-500), hmsm(3, 5, 59, 800));
|
||||
/// assert_eq!(leap + Duration::milliseconds(500), hmsm(3, 5, 59, 1_800));
|
||||
/// assert_eq!(leap + Duration::milliseconds(800), hmsm(3, 6, 0, 100));
|
||||
/// assert_eq!(leap + Duration::seconds(10), hmsm(3, 6, 9, 300));
|
||||
/// assert_eq!(leap + Duration::seconds(-10), hmsm(3, 5, 50, 300));
|
||||
/// assert_eq!(leap + Duration::days(1),
|
||||
/// from_ymd(2016, 7, 9).and_hms_milli(3, 5, 59, 300));
|
||||
/// ~~~~
|
||||
impl Add<Duration> for NaiveDateTime {
|
||||
type Output = NaiveDateTime;
|
||||
|
||||
|
@ -956,6 +1101,44 @@ impl Add<Duration> for NaiveDateTime {
|
|||
}
|
||||
}
|
||||
|
||||
/// A subtraction of `NaiveDateTime` from `NaiveDateTime` yields a `Duration`.
|
||||
/// This does not overflow or underflow at all.
|
||||
///
|
||||
/// As a part of Chrono's [leap second handling](../time/index.html#leap-second-handling),
|
||||
/// the subtraction assumes that **there is no leap second ever**,
|
||||
/// except when any of the `NaiveDateTime`s themselves represents a leap second
|
||||
/// in which case the assumption becomes that
|
||||
/// **there are exactly one (or two) leap second(s) ever**.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::{NaiveDate, Duration};
|
||||
///
|
||||
/// let from_ymd = NaiveDate::from_ymd;
|
||||
///
|
||||
/// let d = from_ymd(2016, 7, 8);
|
||||
/// assert_eq!(d.and_hms(3, 5, 7) - d.and_hms(2, 4, 6),
|
||||
/// Duration::seconds(3600 + 60 + 1));
|
||||
///
|
||||
/// // July 8 is 190th day in the year 2016
|
||||
/// let d0 = from_ymd(2016, 1, 1);
|
||||
/// assert_eq!(d.and_hms_milli(0, 7, 6, 500) - d0.and_hms(0, 0, 0),
|
||||
/// Duration::seconds(189 * 86400 + 7 * 60 + 6) + Duration::milliseconds(500));
|
||||
/// ~~~~
|
||||
///
|
||||
/// Leap seconds are handled, but the subtraction assumes that
|
||||
/// there were no other leap seconds happened.
|
||||
///
|
||||
/// ~~~~
|
||||
/// # use chrono::{NaiveDate, Duration};
|
||||
/// # let from_ymd = NaiveDate::from_ymd;
|
||||
/// let leap = from_ymd(2015, 6, 30).and_hms_milli(23, 59, 59, 1_500);
|
||||
/// assert_eq!(leap - from_ymd(2015, 6, 30).and_hms(23, 0, 0),
|
||||
/// Duration::seconds(3600) + Duration::milliseconds(500));
|
||||
/// assert_eq!(from_ymd(2015, 7, 1).and_hms(1, 0, 0) - leap,
|
||||
/// Duration::seconds(3600) - Duration::milliseconds(500));
|
||||
/// ~~~~
|
||||
impl Sub<NaiveDateTime> for NaiveDateTime {
|
||||
type Output = Duration;
|
||||
|
||||
|
@ -964,6 +1147,54 @@ impl Sub<NaiveDateTime> for NaiveDateTime {
|
|||
}
|
||||
}
|
||||
|
||||
/// A subtraction of `Duration` from `NaiveDateTime` yields another `NaiveDateTime`.
|
||||
/// It is same to the addition with a negated `Duration`.
|
||||
///
|
||||
/// As a part of Chrono's [leap second handling](../time/index.html#leap-second-handling),
|
||||
/// the addition assumes that **there is no leap second ever**,
|
||||
/// except when the `NaiveDateTime` itself represents a leap second
|
||||
/// in which case the assumption becomes that **there is exactly a single leap second ever**.
|
||||
///
|
||||
/// Panics on underflow or overflow.
|
||||
/// Use [`NaiveDateTime::checked_sub`](#method.checked_sub) to detect that.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::{NaiveDate, Duration};
|
||||
///
|
||||
/// let from_ymd = NaiveDate::from_ymd;
|
||||
///
|
||||
/// let d = from_ymd(2016, 7, 8);
|
||||
/// let hms = |h, m, s| d.and_hms(h, m, s);
|
||||
/// assert_eq!(hms(3, 5, 7) - Duration::zero(), hms(3, 5, 7));
|
||||
/// assert_eq!(hms(3, 5, 7) - Duration::seconds(1), hms(3, 5, 6));
|
||||
/// assert_eq!(hms(3, 5, 7) - Duration::seconds(-1), hms(3, 5, 8));
|
||||
/// assert_eq!(hms(3, 5, 7) - Duration::seconds(3600 + 60), hms(2, 4, 7));
|
||||
/// assert_eq!(hms(3, 5, 7) - Duration::seconds(86400),
|
||||
/// from_ymd(2016, 7, 7).and_hms(3, 5, 7));
|
||||
/// assert_eq!(hms(3, 5, 7) - Duration::days(365),
|
||||
/// from_ymd(2015, 7, 9).and_hms(3, 5, 7));
|
||||
///
|
||||
/// let hmsm = |h, m, s, milli| d.and_hms_milli(h, m, s, milli);
|
||||
/// assert_eq!(hmsm(3, 5, 7, 450) - Duration::milliseconds(670), hmsm(3, 5, 6, 780));
|
||||
/// ~~~~
|
||||
///
|
||||
/// Leap seconds are handled,
|
||||
/// but the subtraction assumes that it is the only leap second happened.
|
||||
///
|
||||
/// ~~~~
|
||||
/// # use chrono::{NaiveDate, Duration};
|
||||
/// # let from_ymd = NaiveDate::from_ymd;
|
||||
/// # let hmsm = |h, m, s, milli| from_ymd(2016, 7, 8).and_hms_milli(h, m, s, milli);
|
||||
/// let leap = hmsm(3, 5, 59, 1_300);
|
||||
/// assert_eq!(leap - Duration::zero(), hmsm(3, 5, 59, 1_300));
|
||||
/// assert_eq!(leap - Duration::milliseconds(200), hmsm(3, 5, 59, 1_100));
|
||||
/// assert_eq!(leap - Duration::milliseconds(500), hmsm(3, 5, 59, 800));
|
||||
/// assert_eq!(leap - Duration::seconds(60), hmsm(3, 5, 0, 300));
|
||||
/// assert_eq!(leap - Duration::days(1),
|
||||
/// from_ymd(2016, 7, 7).and_hms_milli(3, 6, 0, 300));
|
||||
/// ~~~~
|
||||
impl Sub<Duration> for NaiveDateTime {
|
||||
type Output = NaiveDateTime;
|
||||
|
||||
|
@ -973,18 +1204,86 @@ impl Sub<Duration> for NaiveDateTime {
|
|||
}
|
||||
}
|
||||
|
||||
/// The `Debug` output of the naive date and time `dt` is same to
|
||||
/// [`dt.format("%Y-%m-%dT%H:%M:%S%.f")`](../../format/strftime/index.html).
|
||||
///
|
||||
/// The string printed can be readily parsed via the `parse` method on `str`.
|
||||
///
|
||||
/// It should be noted that, for leap seconds not on the minute boundary,
|
||||
/// it may print a representation not distinguishable from non-leap seconds.
|
||||
/// This doesn't matter in practice, since such leap seconds never happened.
|
||||
/// (By the time of the first leap second on 1972-06-30,
|
||||
/// every time zone offset around the world has standardized to the 5-minute alignment.)
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::NaiveDate;
|
||||
///
|
||||
/// let dt = NaiveDate::from_ymd(2016, 11, 15).and_hms(7, 39, 24);
|
||||
/// assert_eq!(format!("{:?}", dt), "2016-11-15T07:39:24");
|
||||
/// ~~~~
|
||||
///
|
||||
/// Leap seconds may also be used.
|
||||
///
|
||||
/// ~~~~
|
||||
/// # use chrono::NaiveDate;
|
||||
/// let dt = NaiveDate::from_ymd(2015, 6, 30).and_hms_milli(23, 59, 59, 1_500);
|
||||
/// assert_eq!(format!("{:?}", dt), "2015-06-30T23:59:60.500");
|
||||
/// ~~~~
|
||||
impl fmt::Debug for NaiveDateTime {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{:?}T{:?}", self.date, self.time)
|
||||
}
|
||||
}
|
||||
|
||||
/// The `Debug` output of the naive date and time `dt` is same to
|
||||
/// [`dt.format("%Y-%m-%d %H:%M:%S%.f")`](../../format/strftime/index.html).
|
||||
///
|
||||
/// It should be noted that, for leap seconds not on the minute boundary,
|
||||
/// it may print a representation not distinguishable from non-leap seconds.
|
||||
/// This doesn't matter in practice, since such leap seconds never happened.
|
||||
/// (By the time of the first leap second on 1972-06-30,
|
||||
/// every time zone offset around the world has standardized to the 5-minute alignment.)
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::NaiveDate;
|
||||
///
|
||||
/// let dt = NaiveDate::from_ymd(2016, 11, 15).and_hms(7, 39, 24);
|
||||
/// assert_eq!(format!("{}", dt), "2016-11-15 07:39:24");
|
||||
/// ~~~~
|
||||
///
|
||||
/// Leap seconds may also be used.
|
||||
///
|
||||
/// ~~~~
|
||||
/// # use chrono::NaiveDate;
|
||||
/// let dt = NaiveDate::from_ymd(2015, 6, 30).and_hms_milli(23, 59, 59, 1_500);
|
||||
/// assert_eq!(format!("{}", dt), "2015-06-30 23:59:60.500");
|
||||
/// ~~~~
|
||||
impl fmt::Display for NaiveDateTime {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{} {}", self.date, self.time)
|
||||
}
|
||||
}
|
||||
|
||||
/// Parsing a `str` into a `NaiveDateTime` uses the same format,
|
||||
/// [`%Y-%m-%dT%H:%M:%S%.f`](../../format/strftime/index.html), as in `Debug`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::{NaiveDateTime, NaiveDate};
|
||||
///
|
||||
/// let dt = NaiveDate::from_ymd(2015, 9, 18).and_hms(23, 56, 4);
|
||||
/// assert_eq!("2015-09-18T23:56:04".parse::<NaiveDateTime>(), Ok(dt));
|
||||
///
|
||||
/// let dt = NaiveDate::from_ymd(12345, 6, 7).and_hms_milli(7, 59, 59, 1_500); // leap second
|
||||
/// assert_eq!("+12345-6-7T7:59:60.5".parse::<NaiveDateTime>(), Ok(dt));
|
||||
///
|
||||
/// assert!("foo".parse::<NaiveDateTime>().is_err());
|
||||
/// ~~~~
|
||||
impl str::FromStr for NaiveDateTime {
|
||||
type Err = ParseError;
|
||||
|
||||
|
|
|
@ -129,6 +129,13 @@
|
|||
//! The leap second in the human-readable representation
|
||||
//! will be represented as the second part being 60, as required by ISO 8601.
|
||||
//!
|
||||
//! ~~~~
|
||||
//! use chrono::{UTC, TimeZone};
|
||||
//!
|
||||
//! let dt = UTC.ymd(2015, 6, 30).and_hms_milli(23, 59, 59, 1_000);
|
||||
//! assert_eq!(format!("{:?}", dt), "2015-06-30T23:59:60Z");
|
||||
//! ~~~~
|
||||
//!
|
||||
//! There are hypothetical leap seconds not on the minute boundary
|
||||
//! nevertheless supported by Chrono.
|
||||
//! They are allowed for the sake of completeness and consistency;
|
||||
|
@ -136,6 +143,17 @@
|
|||
//! For such cases the human-readable representation is ambiguous
|
||||
//! and would be read back to the next non-leap second.
|
||||
//!
|
||||
//! ~~~~
|
||||
//! use chrono::{DateTime, UTC, TimeZone};
|
||||
//!
|
||||
//! let dt = UTC.ymd(2015, 6, 30).and_hms_milli(23, 56, 4, 1_000);
|
||||
//! assert_eq!(format!("{:?}", dt), "2015-06-30T23:56:05Z");
|
||||
//!
|
||||
//! let dt = UTC.ymd(2015, 6, 30).and_hms(23, 56, 5);
|
||||
//! assert_eq!(format!("{:?}", dt), "2015-06-30T23:56:05Z");
|
||||
//! assert_eq!(DateTime::parse_from_rfc3339("2015-06-30T23:56:05Z").unwrap(), dt);
|
||||
//! ~~~~
|
||||
//!
|
||||
//! Since Chrono alone cannot determine any existence of leap seconds,
|
||||
//! **there is absolutely no guarantee that the leap second read has actually happened**.
|
||||
|
||||
|
@ -497,6 +515,113 @@ impl NaiveTime {
|
|||
parsed.to_naive_time()
|
||||
}
|
||||
|
||||
/// Adds given `Duration` to the current time,
|
||||
/// and also returns the number of *seconds*
|
||||
/// in the integral number of days ignored from the addition.
|
||||
/// (We cannot return `Duration` because it is subject to overflow or underflow.)
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::{NaiveTime, Duration};
|
||||
///
|
||||
/// let from_hms = NaiveTime::from_hms;
|
||||
///
|
||||
/// assert_eq!(from_hms(3, 4, 5).overflowing_add(Duration::hours(11)),
|
||||
/// (from_hms(14, 4, 5), 0));
|
||||
/// assert_eq!(from_hms(3, 4, 5).overflowing_add(Duration::hours(23)),
|
||||
/// (from_hms(2, 4, 5), 86400));
|
||||
/// assert_eq!(from_hms(3, 4, 5).overflowing_add(Duration::hours(-7)),
|
||||
/// (from_hms(20, 4, 5), -86400));
|
||||
/// ~~~~
|
||||
pub fn overflowing_add(&self, mut rhs: Duration) -> (NaiveTime, i64) {
|
||||
let mut secs = self.secs;
|
||||
let mut frac = self.frac;
|
||||
|
||||
// check if `self` is a leap second and adding `rhs` would escape that leap second.
|
||||
// if it's the case, update `self` and `rhs` to involve no leap second;
|
||||
// otherwise the addition immediately finishes.
|
||||
if frac >= 1_000_000_000 {
|
||||
let rfrac = 2_000_000_000 - frac;
|
||||
if rhs >= Duration::nanoseconds(rfrac as i64) {
|
||||
rhs = rhs - Duration::nanoseconds(rfrac as i64);
|
||||
secs += 1;
|
||||
frac = 0;
|
||||
} else if rhs < Duration::nanoseconds(-(frac as i64)) {
|
||||
rhs = rhs + Duration::nanoseconds(frac as i64);
|
||||
frac = 0;
|
||||
} else {
|
||||
frac = (frac as i64 + rhs.num_nanoseconds().unwrap()) as u32;
|
||||
debug_assert!(frac < 2_000_000_000);
|
||||
return (NaiveTime { secs: secs, frac: frac }, 0);
|
||||
}
|
||||
}
|
||||
debug_assert!(secs <= 86400);
|
||||
debug_assert!(frac < 1_000_000_000);
|
||||
|
||||
let rhssecs = rhs.num_seconds();
|
||||
let rhsfrac = (rhs - Duration::seconds(rhssecs)).num_nanoseconds().unwrap();
|
||||
debug_assert!(Duration::seconds(rhssecs) + Duration::nanoseconds(rhsfrac) == rhs);
|
||||
let rhssecsinday = rhssecs % 86400;
|
||||
let mut morerhssecs = rhssecs - rhssecsinday;
|
||||
let rhssecs = rhssecsinday as i32;
|
||||
let rhsfrac = rhsfrac as i32;
|
||||
debug_assert!(-86400 < rhssecs && rhssecs < 86400);
|
||||
debug_assert!(morerhssecs % 86400 == 0);
|
||||
debug_assert!(-1_000_000_000 < rhsfrac && rhsfrac < 1_000_000_000);
|
||||
|
||||
let mut secs = secs as i32 + rhssecs;
|
||||
let mut frac = frac as i32 + rhsfrac;
|
||||
debug_assert!(-86400 < secs && secs < 2 * 86400);
|
||||
debug_assert!(-1_000_000_000 < frac && frac < 2_000_000_000);
|
||||
|
||||
if frac < 0 {
|
||||
frac += 1_000_000_000;
|
||||
secs -= 1;
|
||||
} else if frac >= 1_000_000_000 {
|
||||
frac -= 1_000_000_000;
|
||||
secs += 1;
|
||||
}
|
||||
debug_assert!(-86400 <= secs && secs < 2 * 86400);
|
||||
debug_assert!(0 <= frac && frac < 1_000_000_000);
|
||||
|
||||
if secs < 0 {
|
||||
secs += 86400;
|
||||
morerhssecs -= 86400;
|
||||
} else if secs >= 86400 {
|
||||
secs -= 86400;
|
||||
morerhssecs += 86400;
|
||||
}
|
||||
debug_assert!(0 <= secs && secs < 86400);
|
||||
|
||||
(NaiveTime { secs: secs as u32, frac: frac as u32 }, morerhssecs)
|
||||
}
|
||||
|
||||
/// Subtracts given `Duration` from the current time,
|
||||
/// and also returns the number of *seconds*
|
||||
/// in the integral number of days ignored from the subtraction.
|
||||
/// (We cannot return `Duration` because it is subject to overflow or underflow.)
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::{NaiveTime, Duration};
|
||||
///
|
||||
/// let from_hms = NaiveTime::from_hms;
|
||||
///
|
||||
/// assert_eq!(from_hms(3, 4, 5).overflowing_sub(Duration::hours(2)),
|
||||
/// (from_hms(1, 4, 5), 0));
|
||||
/// assert_eq!(from_hms(3, 4, 5).overflowing_sub(Duration::hours(17)),
|
||||
/// (from_hms(10, 4, 5), 86400));
|
||||
/// assert_eq!(from_hms(3, 4, 5).overflowing_sub(Duration::hours(-22)),
|
||||
/// (from_hms(1, 4, 5), -86400));
|
||||
/// ~~~~
|
||||
#[inline]
|
||||
pub fn overflowing_sub(&self, rhs: Duration) -> (NaiveTime, i64) {
|
||||
let (time, rhs) = self.overflowing_add(-rhs);
|
||||
(time, -rhs) // safe to negate, rhs is within +/- (2^63 / 1000)
|
||||
}
|
||||
|
||||
/// Formats the time with the specified formatting items.
|
||||
/// Otherwise it is same to the ordinary [`format`](#method.format) method.
|
||||
///
|
||||
|
@ -841,65 +966,9 @@ impl hash::Hash for NaiveTime {
|
|||
impl Add<Duration> for NaiveTime {
|
||||
type Output = NaiveTime;
|
||||
|
||||
fn add(self, mut rhs: Duration) -> NaiveTime {
|
||||
let mut secs = self.secs;
|
||||
let mut frac = self.frac;
|
||||
|
||||
// check if `self` is a leap second and adding `rhs` would escape that leap second.
|
||||
// if it's the case, update `self` and `rhs` to involve no leap second;
|
||||
// otherwise the addition immediately finishes.
|
||||
if frac >= 1_000_000_000 {
|
||||
let rfrac = 2_000_000_000 - frac;
|
||||
if rhs >= Duration::nanoseconds(rfrac as i64) {
|
||||
rhs = rhs - Duration::nanoseconds(rfrac as i64);
|
||||
secs += 1;
|
||||
frac = 0;
|
||||
} else if rhs < Duration::nanoseconds(-(frac as i64)) {
|
||||
rhs = rhs + Duration::nanoseconds(frac as i64);
|
||||
frac = 0;
|
||||
} else {
|
||||
frac = (frac as i64 + rhs.num_nanoseconds().unwrap()) as u32;
|
||||
debug_assert!(frac < 2_000_000_000);
|
||||
return NaiveTime { secs: secs, frac: frac };
|
||||
}
|
||||
}
|
||||
debug_assert!(secs <= 86400);
|
||||
debug_assert!(frac < 1_000_000_000);
|
||||
|
||||
let rhssecs = rhs.num_seconds();
|
||||
let rhsfrac = (rhs - Duration::seconds(rhssecs)).num_nanoseconds().unwrap();
|
||||
debug_assert!(Duration::seconds(rhssecs) + Duration::nanoseconds(rhsfrac) == rhs);
|
||||
let rhssecs = (rhssecs % 86400) as i32;
|
||||
let rhsfrac = rhsfrac as i32;
|
||||
debug_assert!(-86400 < rhssecs && rhssecs < 86400);
|
||||
debug_assert!(-1_000_000_000 < rhsfrac && rhsfrac < 1_000_000_000);
|
||||
|
||||
let mut secs = secs as i32 + rhssecs;
|
||||
let mut frac = frac as i32 + rhsfrac;
|
||||
debug_assert!(-86400 < secs && secs <= 2 * 86400 + 1);
|
||||
debug_assert!(-1_000_000_000 < frac && frac < 2_000_000_000);
|
||||
|
||||
if frac < 0 {
|
||||
frac += 1_000_000_000;
|
||||
secs -= 1;
|
||||
} else if frac >= 1_000_000_000 {
|
||||
frac -= 1_000_000_000;
|
||||
secs += 1;
|
||||
}
|
||||
debug_assert!(-86400 <= secs && secs <= 2 * 86400 + 1);
|
||||
debug_assert!(0 <= frac && frac < 1_000_000_000);
|
||||
|
||||
if secs < 0 {
|
||||
secs += 86400 * 2;
|
||||
}
|
||||
if secs >= 86400 * 2 {
|
||||
secs -= 86400 * 2;
|
||||
} else if secs >= 86400 {
|
||||
secs -= 86400;
|
||||
}
|
||||
debug_assert!(0 <= secs && secs < 86400);
|
||||
|
||||
NaiveTime { secs: secs as u32, frac: frac as u32 }
|
||||
#[inline]
|
||||
fn add(self, rhs: Duration) -> NaiveTime {
|
||||
self.overflowing_add(rhs).0
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -907,7 +976,7 @@ impl Add<Duration> for NaiveTime {
|
|||
/// This does not overflow or underflow at all.
|
||||
///
|
||||
/// As a part of Chrono's [leap second handling](./index.html#leap-second-handling),
|
||||
/// the addition assumes that **there is no leap second ever**,
|
||||
/// the subtraction assumes that **there is no leap second ever**,
|
||||
/// except when any of the `NaiveTime`s themselves represents a leap second
|
||||
/// in which case the assumption becomes that
|
||||
/// **there are exactly one (or two) leap second(s) ever**.
|
||||
|
@ -1021,7 +1090,9 @@ impl Sub<Duration> for NaiveTime {
|
|||
type Output = NaiveTime;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: Duration) -> NaiveTime { self.add(-rhs) }
|
||||
fn sub(self, rhs: Duration) -> NaiveTime {
|
||||
self.overflowing_sub(rhs).0
|
||||
}
|
||||
}
|
||||
|
||||
/// The `Debug` output of the naive time `t` is same to
|
||||
|
@ -1417,6 +1488,10 @@ mod tests {
|
|||
|
||||
check!(hmsm(3, 5, 7, 900), Duration::zero(), hmsm(3, 5, 7, 900));
|
||||
check!(hmsm(3, 5, 7, 900), Duration::milliseconds(100), hmsm(3, 5, 8, 0));
|
||||
check!(hmsm(3, 5, 7, 1_300), Duration::milliseconds(-1800), hmsm(3, 5, 6, 500));
|
||||
check!(hmsm(3, 5, 7, 1_300), Duration::milliseconds(-800), hmsm(3, 5, 7, 500));
|
||||
check!(hmsm(3, 5, 7, 1_300), Duration::milliseconds(-100), hmsm(3, 5, 7, 1_200));
|
||||
check!(hmsm(3, 5, 7, 1_300), Duration::milliseconds(100), hmsm(3, 5, 7, 1_400));
|
||||
check!(hmsm(3, 5, 7, 1_300), Duration::milliseconds(800), hmsm(3, 5, 8, 100));
|
||||
check!(hmsm(3, 5, 7, 1_300), Duration::milliseconds(1800), hmsm(3, 5, 9, 100));
|
||||
check!(hmsm(3, 5, 7, 900), Duration::seconds(86399), hmsm(3, 5, 6, 900)); // overwrap
|
||||
|
@ -1430,6 +1505,24 @@ mod tests {
|
|||
check!(hmsm(0, 0, 0, 0), Duration::milliseconds(-9990), hmsm(23, 59, 50, 10));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_time_overflowing_add() {
|
||||
let hmsm = NaiveTime::from_hms_milli;
|
||||
|
||||
assert_eq!(hmsm(3, 4, 5, 678).overflowing_add(Duration::hours(11)),
|
||||
(hmsm(14, 4, 5, 678), 0));
|
||||
assert_eq!(hmsm(3, 4, 5, 678).overflowing_add(Duration::hours(23)),
|
||||
(hmsm(2, 4, 5, 678), 86400));
|
||||
assert_eq!(hmsm(3, 4, 5, 678).overflowing_add(Duration::hours(-7)),
|
||||
(hmsm(20, 4, 5, 678), -86400));
|
||||
|
||||
// overflowing_add with leap seconds may be counter-intuitive
|
||||
assert_eq!(hmsm(3, 4, 5, 1_678).overflowing_add(Duration::days(1)),
|
||||
(hmsm(3, 4, 5, 678), 86400));
|
||||
assert_eq!(hmsm(3, 4, 5, 1_678).overflowing_add(Duration::days(-1)),
|
||||
(hmsm(3, 4, 6, 678), -86400));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_time_sub() {
|
||||
macro_rules! check {
|
||||
|
|
Loading…
Reference in New Issue