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:
Kang Seonghoon 2016-08-17 00:40:36 +09:00
parent 8b382fca45
commit 582f1166f2
No known key found for this signature in database
GPG Key ID: 82440FABA6709020
3 changed files with 493 additions and 91 deletions

View File

@ -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> {

View File

@ -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;

View File

@ -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 {