From c368bf16d0a7bab40ae4632f7b3d180ca7592e0e Mon Sep 17 00:00:00 2001 From: Jethro Beekman Date: Fri, 30 Mar 2018 16:04:26 -0700 Subject: [PATCH 1/4] Adjust features selection in CI --- .travis.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.travis.sh b/.travis.sh index 120cbbe0c8..872a5b19a1 100755 --- a/.travis.sh +++ b/.travis.sh @@ -33,8 +33,10 @@ build_and_test() { TZ=ACST-9:30 channel test -v --lib channel build -v --features rustc-serialize TZ=EST4 channel test -v --features rustc-serialize --lib - channel build -v --features 'serde bincode' - TZ=UTC0 channel test -v --features 'serde bincode' + channel build -v --features serde + TZ=UTC0 channel test -v --features serde --lib + channel build -v --features serde,rustc-serialize + TZ=Asia/Katmandu channel test -v --features serde,rustc-serialize } build_only() { From 5e730ebe4d06e00054fe01c741f92f527733a6a9 Mon Sep 17 00:00:00 2001 From: Jethro Beekman Date: Fri, 24 Feb 2017 15:19:02 -0800 Subject: [PATCH 2/4] Vendor oldtime::duration module from time crate https://github.com/rust-lang-deprecated/time/blob/master/src/duration.rs --- src/oldtime.rs | 648 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 648 insertions(+) create mode 100644 src/oldtime.rs diff --git a/src/oldtime.rs b/src/oldtime.rs new file mode 100644 index 0000000000..33050aee49 --- /dev/null +++ b/src/oldtime.rs @@ -0,0 +1,648 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Temporal quantification + +use std::{fmt, i64}; +use std::error::Error; +use std::ops::{Add, Sub, Mul, Div, Neg, FnOnce}; +use std::time::Duration as StdDuration; + +/// The number of nanoseconds in a microsecond. +const NANOS_PER_MICRO: i32 = 1000; +/// The number of nanoseconds in a millisecond. +const NANOS_PER_MILLI: i32 = 1000_000; +/// The number of nanoseconds in seconds. +const NANOS_PER_SEC: i32 = 1_000_000_000; +/// The number of microseconds per second. +const MICROS_PER_SEC: i64 = 1000_000; +/// The number of milliseconds per second. +const MILLIS_PER_SEC: i64 = 1000; +/// The number of seconds in a minute. +const SECS_PER_MINUTE: i64 = 60; +/// The number of seconds in an hour. +const SECS_PER_HOUR: i64 = 3600; +/// The number of (non-leap) seconds in days. +const SECS_PER_DAY: i64 = 86400; +/// The number of (non-leap) seconds in a week. +const SECS_PER_WEEK: i64 = 604800; + +macro_rules! try_opt { + ($e:expr) => (match $e { Some(v) => v, None => return None }) +} + + +/// ISO 8601 time duration with nanosecond precision. +/// This also allows for the negative duration; see individual methods for details. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] +pub struct Duration { + secs: i64, + nanos: i32, // Always 0 <= nanos < NANOS_PER_SEC +} + +/// The minimum possible `Duration`: `i64::MIN` milliseconds. +pub const MIN: Duration = Duration { + secs: i64::MIN / MILLIS_PER_SEC - 1, + nanos: NANOS_PER_SEC + (i64::MIN % MILLIS_PER_SEC) as i32 * NANOS_PER_MILLI +}; + +/// The maximum possible `Duration`: `i64::MAX` milliseconds. +pub const MAX: Duration = Duration { + secs: i64::MAX / MILLIS_PER_SEC, + nanos: (i64::MAX % MILLIS_PER_SEC) as i32 * NANOS_PER_MILLI +}; + +impl Duration { + /// Makes a new `Duration` with given number of weeks. + /// Equivalent to `Duration::seconds(weeks * 7 * 24 * 60 * 60)` with overflow checks. + /// Panics when the duration is out of bounds. + #[inline] + pub fn weeks(weeks: i64) -> Duration { + let secs = weeks.checked_mul(SECS_PER_WEEK).expect("Duration::weeks out of bounds"); + Duration::seconds(secs) + } + + /// Makes a new `Duration` with given number of days. + /// Equivalent to `Duration::seconds(days * 24 * 60 * 60)` with overflow checks. + /// Panics when the duration is out of bounds. + #[inline] + pub fn days(days: i64) -> Duration { + let secs = days.checked_mul(SECS_PER_DAY).expect("Duration::days out of bounds"); + Duration::seconds(secs) + } + + /// Makes a new `Duration` with given number of hours. + /// Equivalent to `Duration::seconds(hours * 60 * 60)` with overflow checks. + /// Panics when the duration is out of bounds. + #[inline] + pub fn hours(hours: i64) -> Duration { + let secs = hours.checked_mul(SECS_PER_HOUR).expect("Duration::hours ouf of bounds"); + Duration::seconds(secs) + } + + /// Makes a new `Duration` with given number of minutes. + /// Equivalent to `Duration::seconds(minutes * 60)` with overflow checks. + /// Panics when the duration is out of bounds. + #[inline] + pub fn minutes(minutes: i64) -> Duration { + let secs = minutes.checked_mul(SECS_PER_MINUTE).expect("Duration::minutes out of bounds"); + Duration::seconds(secs) + } + + /// Makes a new `Duration` with given number of seconds. + /// Panics when the duration is more than `i64::MAX` milliseconds + /// or less than `i64::MIN` milliseconds. + #[inline] + pub fn seconds(seconds: i64) -> Duration { + let d = Duration { secs: seconds, nanos: 0 }; + if d < MIN || d > MAX { + panic!("Duration::seconds out of bounds"); + } + d + } + + /// Makes a new `Duration` with given number of milliseconds. + #[inline] + pub fn milliseconds(milliseconds: i64) -> Duration { + let (secs, millis) = div_mod_floor_64(milliseconds, MILLIS_PER_SEC); + let nanos = millis as i32 * NANOS_PER_MILLI; + Duration { secs: secs, nanos: nanos } + } + + /// Makes a new `Duration` with given number of microseconds. + #[inline] + pub fn microseconds(microseconds: i64) -> Duration { + let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC); + let nanos = micros as i32 * NANOS_PER_MICRO; + Duration { secs: secs, nanos: nanos } + } + + /// Makes a new `Duration` with given number of nanoseconds. + #[inline] + pub fn nanoseconds(nanos: i64) -> Duration { + let (secs, nanos) = div_mod_floor_64(nanos, NANOS_PER_SEC as i64); + Duration { secs: secs, nanos: nanos as i32 } + } + + /// Runs a closure, returning the duration of time it took to run the + /// closure. + pub fn span(f: F) -> Duration where F: FnOnce() { + let before = super::precise_time_ns(); + f(); + Duration::nanoseconds((super::precise_time_ns() - before) as i64) + } + + /// Returns the total number of whole weeks in the duration. + #[inline] + pub fn num_weeks(&self) -> i64 { + self.num_days() / 7 + } + + /// Returns the total number of whole days in the duration. + pub fn num_days(&self) -> i64 { + self.num_seconds() / SECS_PER_DAY + } + + /// Returns the total number of whole hours in the duration. + #[inline] + pub fn num_hours(&self) -> i64 { + self.num_seconds() / SECS_PER_HOUR + } + + /// Returns the total number of whole minutes in the duration. + #[inline] + pub fn num_minutes(&self) -> i64 { + self.num_seconds() / SECS_PER_MINUTE + } + + /// Returns the total number of whole seconds in the duration. + pub fn num_seconds(&self) -> i64 { + // If secs is negative, nanos should be subtracted from the duration. + if self.secs < 0 && self.nanos > 0 { + self.secs + 1 + } else { + self.secs + } + } + + /// Returns the number of nanoseconds such that + /// `nanos_mod_sec() + num_seconds() * NANOS_PER_SEC` is the total number of + /// nanoseconds in the duration. + fn nanos_mod_sec(&self) -> i32 { + if self.secs < 0 && self.nanos > 0 { + self.nanos - NANOS_PER_SEC + } else { + self.nanos + } + } + + /// Returns the total number of whole milliseconds in the duration, + pub fn num_milliseconds(&self) -> i64 { + // A proper Duration will not overflow, because MIN and MAX are defined + // such that the range is exactly i64 milliseconds. + let secs_part = self.num_seconds() * MILLIS_PER_SEC; + let nanos_part = self.nanos_mod_sec() / NANOS_PER_MILLI; + secs_part + nanos_part as i64 + } + + /// Returns the total number of whole microseconds in the duration, + /// or `None` on overflow (exceeding 2^63 microseconds in either direction). + pub fn num_microseconds(&self) -> Option { + let secs_part = try_opt!(self.num_seconds().checked_mul(MICROS_PER_SEC)); + let nanos_part = self.nanos_mod_sec() / NANOS_PER_MICRO; + secs_part.checked_add(nanos_part as i64) + } + + /// Returns the total number of whole nanoseconds in the duration, + /// or `None` on overflow (exceeding 2^63 nanoseconds in either direction). + pub fn num_nanoseconds(&self) -> Option { + let secs_part = try_opt!(self.num_seconds().checked_mul(NANOS_PER_SEC as i64)); + let nanos_part = self.nanos_mod_sec(); + secs_part.checked_add(nanos_part as i64) + } + + /// Add two durations, returning `None` if overflow occurred. + pub fn checked_add(&self, rhs: &Duration) -> Option { + let mut secs = try_opt!(self.secs.checked_add(rhs.secs)); + let mut nanos = self.nanos + rhs.nanos; + if nanos >= NANOS_PER_SEC { + nanos -= NANOS_PER_SEC; + secs = try_opt!(secs.checked_add(1)); + } + let d = Duration { secs: secs, nanos: nanos }; + // Even if d is within the bounds of i64 seconds, + // it might still overflow i64 milliseconds. + if d < MIN || d > MAX { None } else { Some(d) } + } + + /// Subtract two durations, returning `None` if overflow occurred. + pub fn checked_sub(&self, rhs: &Duration) -> Option { + let mut secs = try_opt!(self.secs.checked_sub(rhs.secs)); + let mut nanos = self.nanos - rhs.nanos; + if nanos < 0 { + nanos += NANOS_PER_SEC; + secs = try_opt!(secs.checked_sub(1)); + } + let d = Duration { secs: secs, nanos: nanos }; + // Even if d is within the bounds of i64 seconds, + // it might still overflow i64 milliseconds. + if d < MIN || d > MAX { None } else { Some(d) } + } + + /// The minimum possible `Duration`: `i64::MIN` milliseconds. + #[inline] + pub fn min_value() -> Duration { MIN } + + /// The maximum possible `Duration`: `i64::MAX` milliseconds. + #[inline] + pub fn max_value() -> Duration { MAX } + + /// A duration where the stored seconds and nanoseconds are equal to zero. + #[inline] + pub fn zero() -> Duration { + Duration { secs: 0, nanos: 0 } + } + + /// Returns `true` if the duration equals `Duration::zero()`. + #[inline] + pub fn is_zero(&self) -> bool { + self.secs == 0 && self.nanos == 0 + } + + /// Creates a `time::Duration` object from `std::time::Duration` + /// + /// This function errors when original duration is larger than the maximum + /// value supported for this type. + pub fn from_std(duration: StdDuration) -> Result { + // We need to check secs as u64 before coercing to i64 + if duration.as_secs() > MAX.secs as u64 { + return Err(OutOfRangeError(())); + } + let d = Duration { + secs: duration.as_secs() as i64, + nanos: duration.subsec_nanos() as i32, + }; + if d > MAX { + return Err(OutOfRangeError(())); + } + Ok(d) + } + + /// Creates a `std::time::Duration` object from `time::Duration` + /// + /// This function errors when duration is less than zero. As standard + /// library implementation is limited to non-negative values. + pub fn to_std(&self) -> Result { + if self.secs < 0 { + return Err(OutOfRangeError(())); + } + Ok(StdDuration::new(self.secs as u64, self.nanos as u32)) + } +} + +impl Neg for Duration { + type Output = Duration; + + #[inline] + fn neg(self) -> Duration { + if self.nanos == 0 { + Duration { secs: -self.secs, nanos: 0 } + } else { + Duration { secs: -self.secs - 1, nanos: NANOS_PER_SEC - self.nanos } + } + } +} + +impl Add for Duration { + type Output = Duration; + + fn add(self, rhs: Duration) -> Duration { + let mut secs = self.secs + rhs.secs; + let mut nanos = self.nanos + rhs.nanos; + if nanos >= NANOS_PER_SEC { + nanos -= NANOS_PER_SEC; + secs += 1; + } + Duration { secs: secs, nanos: nanos } + } +} + +impl Sub for Duration { + type Output = Duration; + + fn sub(self, rhs: Duration) -> Duration { + let mut secs = self.secs - rhs.secs; + let mut nanos = self.nanos - rhs.nanos; + if nanos < 0 { + nanos += NANOS_PER_SEC; + secs -= 1; + } + Duration { secs: secs, nanos: nanos } + } +} + +impl Mul for Duration { + type Output = Duration; + + fn mul(self, rhs: i32) -> Duration { + // Multiply nanoseconds as i64, because it cannot overflow that way. + let total_nanos = self.nanos as i64 * rhs as i64; + let (extra_secs, nanos) = div_mod_floor_64(total_nanos, NANOS_PER_SEC as i64); + let secs = self.secs * rhs as i64 + extra_secs; + Duration { secs: secs, nanos: nanos as i32 } + } +} + +impl Div for Duration { + type Output = Duration; + + fn div(self, rhs: i32) -> Duration { + let mut secs = self.secs / rhs as i64; + let carry = self.secs - secs * rhs as i64; + let extra_nanos = carry * NANOS_PER_SEC as i64 / rhs as i64; + let mut nanos = self.nanos / rhs + extra_nanos as i32; + if nanos >= NANOS_PER_SEC { + nanos -= NANOS_PER_SEC; + secs += 1; + } + if nanos < 0 { + nanos += NANOS_PER_SEC; + secs -= 1; + } + Duration { secs: secs, nanos: nanos } + } +} + +impl fmt::Display for Duration { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // technically speaking, negative duration is not valid ISO 8601, + // but we need to print it anyway. + let (abs, sign) = if self.secs < 0 { (-*self, "-") } else { (*self, "") }; + + let days = abs.secs / SECS_PER_DAY; + let secs = abs.secs - days * SECS_PER_DAY; + let hasdate = days != 0; + let hastime = (secs != 0 || abs.nanos != 0) || !hasdate; + + try!(write!(f, "{}P", sign)); + + if hasdate { + try!(write!(f, "{}D", days)); + } + if hastime { + if abs.nanos == 0 { + try!(write!(f, "T{}S", secs)); + } else if abs.nanos % NANOS_PER_MILLI == 0 { + try!(write!(f, "T{}.{:03}S", secs, abs.nanos / NANOS_PER_MILLI)); + } else if abs.nanos % NANOS_PER_MICRO == 0 { + try!(write!(f, "T{}.{:06}S", secs, abs.nanos / NANOS_PER_MICRO)); + } else { + try!(write!(f, "T{}.{:09}S", secs, abs.nanos)); + } + } + Ok(()) + } +} + +/// Represents error when converting `Duration` to/from a standard library +/// implementation +/// +/// The `std::time::Duration` supports a range from zero to `u64::MAX` +/// *seconds*, while this module supports signed range of up to +/// `i64::MAX` of *milliseconds*. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct OutOfRangeError(()); + +impl fmt::Display for OutOfRangeError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.description()) + } +} + +impl Error for OutOfRangeError { + fn description(&self) -> &str { + "Source duration value is out of range for the target type" + } +} + +// Copied from libnum +#[inline] +fn div_mod_floor_64(this: i64, other: i64) -> (i64, i64) { + (div_floor_64(this, other), mod_floor_64(this, other)) +} + +#[inline] +fn div_floor_64(this: i64, other: i64) -> i64 { + match div_rem_64(this, other) { + (d, r) if (r > 0 && other < 0) + || (r < 0 && other > 0) => d - 1, + (d, _) => d, + } +} + +#[inline] +fn mod_floor_64(this: i64, other: i64) -> i64 { + match this % other { + r if (r > 0 && other < 0) + || (r < 0 && other > 0) => r + other, + r => r, + } +} + +#[inline] +fn div_rem_64(this: i64, other: i64) -> (i64, i64) { + (this / other, this % other) +} + +#[cfg(test)] +mod tests { + use super::{Duration, MIN, MAX, OutOfRangeError}; + use std::{i32, i64}; + use std::time::Duration as StdDuration; + + #[test] + fn test_duration() { + assert!(Duration::seconds(1) != Duration::zero()); + assert_eq!(Duration::seconds(1) + Duration::seconds(2), Duration::seconds(3)); + assert_eq!(Duration::seconds(86399) + Duration::seconds(4), + Duration::days(1) + Duration::seconds(3)); + assert_eq!(Duration::days(10) - Duration::seconds(1000), Duration::seconds(863000)); + assert_eq!(Duration::days(10) - Duration::seconds(1000000), Duration::seconds(-136000)); + assert_eq!(Duration::days(2) + Duration::seconds(86399) + + Duration::nanoseconds(1234567890), + Duration::days(3) + Duration::nanoseconds(234567890)); + assert_eq!(-Duration::days(3), Duration::days(-3)); + assert_eq!(-(Duration::days(3) + Duration::seconds(70)), + Duration::days(-4) + Duration::seconds(86400-70)); + } + + #[test] + fn test_duration_num_days() { + assert_eq!(Duration::zero().num_days(), 0); + assert_eq!(Duration::days(1).num_days(), 1); + assert_eq!(Duration::days(-1).num_days(), -1); + assert_eq!(Duration::seconds(86399).num_days(), 0); + assert_eq!(Duration::seconds(86401).num_days(), 1); + assert_eq!(Duration::seconds(-86399).num_days(), 0); + assert_eq!(Duration::seconds(-86401).num_days(), -1); + assert_eq!(Duration::days(i32::MAX as i64).num_days(), i32::MAX as i64); + assert_eq!(Duration::days(i32::MIN as i64).num_days(), i32::MIN as i64); + } + + #[test] + fn test_duration_num_seconds() { + assert_eq!(Duration::zero().num_seconds(), 0); + assert_eq!(Duration::seconds(1).num_seconds(), 1); + assert_eq!(Duration::seconds(-1).num_seconds(), -1); + assert_eq!(Duration::milliseconds(999).num_seconds(), 0); + assert_eq!(Duration::milliseconds(1001).num_seconds(), 1); + assert_eq!(Duration::milliseconds(-999).num_seconds(), 0); + assert_eq!(Duration::milliseconds(-1001).num_seconds(), -1); + } + + #[test] + fn test_duration_num_milliseconds() { + assert_eq!(Duration::zero().num_milliseconds(), 0); + assert_eq!(Duration::milliseconds(1).num_milliseconds(), 1); + assert_eq!(Duration::milliseconds(-1).num_milliseconds(), -1); + assert_eq!(Duration::microseconds(999).num_milliseconds(), 0); + assert_eq!(Duration::microseconds(1001).num_milliseconds(), 1); + assert_eq!(Duration::microseconds(-999).num_milliseconds(), 0); + assert_eq!(Duration::microseconds(-1001).num_milliseconds(), -1); + assert_eq!(Duration::milliseconds(i64::MAX).num_milliseconds(), i64::MAX); + assert_eq!(Duration::milliseconds(i64::MIN).num_milliseconds(), i64::MIN); + assert_eq!(MAX.num_milliseconds(), i64::MAX); + assert_eq!(MIN.num_milliseconds(), i64::MIN); + } + + #[test] + fn test_duration_num_microseconds() { + assert_eq!(Duration::zero().num_microseconds(), Some(0)); + assert_eq!(Duration::microseconds(1).num_microseconds(), Some(1)); + assert_eq!(Duration::microseconds(-1).num_microseconds(), Some(-1)); + assert_eq!(Duration::nanoseconds(999).num_microseconds(), Some(0)); + assert_eq!(Duration::nanoseconds(1001).num_microseconds(), Some(1)); + assert_eq!(Duration::nanoseconds(-999).num_microseconds(), Some(0)); + assert_eq!(Duration::nanoseconds(-1001).num_microseconds(), Some(-1)); + assert_eq!(Duration::microseconds(i64::MAX).num_microseconds(), Some(i64::MAX)); + assert_eq!(Duration::microseconds(i64::MIN).num_microseconds(), Some(i64::MIN)); + assert_eq!(MAX.num_microseconds(), None); + assert_eq!(MIN.num_microseconds(), None); + + // overflow checks + const MICROS_PER_DAY: i64 = 86400_000_000; + assert_eq!(Duration::days(i64::MAX / MICROS_PER_DAY).num_microseconds(), + Some(i64::MAX / MICROS_PER_DAY * MICROS_PER_DAY)); + assert_eq!(Duration::days(i64::MIN / MICROS_PER_DAY).num_microseconds(), + Some(i64::MIN / MICROS_PER_DAY * MICROS_PER_DAY)); + assert_eq!(Duration::days(i64::MAX / MICROS_PER_DAY + 1).num_microseconds(), None); + assert_eq!(Duration::days(i64::MIN / MICROS_PER_DAY - 1).num_microseconds(), None); + } + + #[test] + fn test_duration_num_nanoseconds() { + assert_eq!(Duration::zero().num_nanoseconds(), Some(0)); + assert_eq!(Duration::nanoseconds(1).num_nanoseconds(), Some(1)); + assert_eq!(Duration::nanoseconds(-1).num_nanoseconds(), Some(-1)); + assert_eq!(Duration::nanoseconds(i64::MAX).num_nanoseconds(), Some(i64::MAX)); + assert_eq!(Duration::nanoseconds(i64::MIN).num_nanoseconds(), Some(i64::MIN)); + assert_eq!(MAX.num_nanoseconds(), None); + assert_eq!(MIN.num_nanoseconds(), None); + + // overflow checks + const NANOS_PER_DAY: i64 = 86400_000_000_000; + assert_eq!(Duration::days(i64::MAX / NANOS_PER_DAY).num_nanoseconds(), + Some(i64::MAX / NANOS_PER_DAY * NANOS_PER_DAY)); + assert_eq!(Duration::days(i64::MIN / NANOS_PER_DAY).num_nanoseconds(), + Some(i64::MIN / NANOS_PER_DAY * NANOS_PER_DAY)); + assert_eq!(Duration::days(i64::MAX / NANOS_PER_DAY + 1).num_nanoseconds(), None); + assert_eq!(Duration::days(i64::MIN / NANOS_PER_DAY - 1).num_nanoseconds(), None); + } + + #[test] + fn test_duration_checked_ops() { + assert_eq!(Duration::milliseconds(i64::MAX - 1).checked_add(&Duration::microseconds(999)), + Some(Duration::milliseconds(i64::MAX - 2) + Duration::microseconds(1999))); + assert!(Duration::milliseconds(i64::MAX).checked_add(&Duration::microseconds(1000)) + .is_none()); + + assert_eq!(Duration::milliseconds(i64::MIN).checked_sub(&Duration::milliseconds(0)), + Some(Duration::milliseconds(i64::MIN))); + assert!(Duration::milliseconds(i64::MIN).checked_sub(&Duration::milliseconds(1)) + .is_none()); + } + + #[test] + fn test_duration_mul() { + assert_eq!(Duration::zero() * i32::MAX, Duration::zero()); + assert_eq!(Duration::zero() * i32::MIN, Duration::zero()); + assert_eq!(Duration::nanoseconds(1) * 0, Duration::zero()); + assert_eq!(Duration::nanoseconds(1) * 1, Duration::nanoseconds(1)); + assert_eq!(Duration::nanoseconds(1) * 1_000_000_000, Duration::seconds(1)); + assert_eq!(Duration::nanoseconds(1) * -1_000_000_000, -Duration::seconds(1)); + assert_eq!(-Duration::nanoseconds(1) * 1_000_000_000, -Duration::seconds(1)); + assert_eq!(Duration::nanoseconds(30) * 333_333_333, + Duration::seconds(10) - Duration::nanoseconds(10)); + assert_eq!((Duration::nanoseconds(1) + Duration::seconds(1) + Duration::days(1)) * 3, + Duration::nanoseconds(3) + Duration::seconds(3) + Duration::days(3)); + assert_eq!(Duration::milliseconds(1500) * -2, Duration::seconds(-3)); + assert_eq!(Duration::milliseconds(-1500) * 2, Duration::seconds(-3)); + } + + #[test] + fn test_duration_div() { + assert_eq!(Duration::zero() / i32::MAX, Duration::zero()); + assert_eq!(Duration::zero() / i32::MIN, Duration::zero()); + assert_eq!(Duration::nanoseconds(123_456_789) / 1, Duration::nanoseconds(123_456_789)); + assert_eq!(Duration::nanoseconds(123_456_789) / -1, -Duration::nanoseconds(123_456_789)); + assert_eq!(-Duration::nanoseconds(123_456_789) / -1, Duration::nanoseconds(123_456_789)); + assert_eq!(-Duration::nanoseconds(123_456_789) / 1, -Duration::nanoseconds(123_456_789)); + assert_eq!(Duration::seconds(1) / 3, Duration::nanoseconds(333_333_333)); + assert_eq!(Duration::seconds(4) / 3, Duration::nanoseconds(1_333_333_333)); + assert_eq!(Duration::seconds(-1) / 2, Duration::milliseconds(-500)); + assert_eq!(Duration::seconds(1) / -2, Duration::milliseconds(-500)); + assert_eq!(Duration::seconds(-1) / -2, Duration::milliseconds(500)); + assert_eq!(Duration::seconds(-4) / 3, Duration::nanoseconds(-1_333_333_333)); + assert_eq!(Duration::seconds(-4) / -3, Duration::nanoseconds(1_333_333_333)); + } + + #[test] + fn test_duration_fmt() { + assert_eq!(Duration::zero().to_string(), "PT0S"); + assert_eq!(Duration::days(42).to_string(), "P42D"); + assert_eq!(Duration::days(-42).to_string(), "-P42D"); + assert_eq!(Duration::seconds(42).to_string(), "PT42S"); + assert_eq!(Duration::milliseconds(42).to_string(), "PT0.042S"); + assert_eq!(Duration::microseconds(42).to_string(), "PT0.000042S"); + assert_eq!(Duration::nanoseconds(42).to_string(), "PT0.000000042S"); + assert_eq!((Duration::days(7) + Duration::milliseconds(6543)).to_string(), + "P7DT6.543S"); + assert_eq!(Duration::seconds(-86401).to_string(), "-P1DT1S"); + assert_eq!(Duration::nanoseconds(-1).to_string(), "-PT0.000000001S"); + + // the format specifier should have no effect on `Duration` + assert_eq!(format!("{:30}", Duration::days(1) + Duration::milliseconds(2345)), + "P1DT2.345S"); + } + + #[test] + fn test_to_std() { + assert_eq!(Duration::seconds(1).to_std(), Ok(StdDuration::new(1, 0))); + assert_eq!(Duration::seconds(86401).to_std(), Ok(StdDuration::new(86401, 0))); + assert_eq!(Duration::milliseconds(123).to_std(), Ok(StdDuration::new(0, 123000000))); + assert_eq!(Duration::milliseconds(123765).to_std(), Ok(StdDuration::new(123, 765000000))); + assert_eq!(Duration::nanoseconds(777).to_std(), Ok(StdDuration::new(0, 777))); + assert_eq!(MAX.to_std(), Ok(StdDuration::new(9223372036854775, 807000000))); + assert_eq!(Duration::seconds(-1).to_std(), + Err(OutOfRangeError(()))); + assert_eq!(Duration::milliseconds(-1).to_std(), + Err(OutOfRangeError(()))); + } + + #[test] + fn test_from_std() { + assert_eq!(Ok(Duration::seconds(1)), + Duration::from_std(StdDuration::new(1, 0))); + assert_eq!(Ok(Duration::seconds(86401)), + Duration::from_std(StdDuration::new(86401, 0))); + assert_eq!(Ok(Duration::milliseconds(123)), + Duration::from_std(StdDuration::new(0, 123000000))); + assert_eq!(Ok(Duration::milliseconds(123765)), + Duration::from_std(StdDuration::new(123, 765000000))); + assert_eq!(Ok(Duration::nanoseconds(777)), + Duration::from_std(StdDuration::new(0, 777))); + assert_eq!(Ok(MAX), + Duration::from_std(StdDuration::new(9223372036854775, 807000000))); + assert_eq!(Duration::from_std(StdDuration::new(9223372036854776, 0)), + Err(OutOfRangeError(()))); + assert_eq!(Duration::from_std(StdDuration::new(9223372036854775, 807000001)), + Err(OutOfRangeError(()))); + } +} From 6c398ae6f40526994918a271e8193a72768a1ff7 Mon Sep 17 00:00:00 2001 From: Jethro Beekman Date: Fri, 24 Feb 2017 15:19:48 -0800 Subject: [PATCH 3/4] Remove dependencies from oldtime::Duration on rest of time crate --- src/oldtime.rs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/oldtime.rs b/src/oldtime.rs index 33050aee49..2bdc2f64ef 100644 --- a/src/oldtime.rs +++ b/src/oldtime.rs @@ -12,7 +12,7 @@ use std::{fmt, i64}; use std::error::Error; -use std::ops::{Add, Sub, Mul, Div, Neg, FnOnce}; +use std::ops::{Add, Sub, Mul, Div, Neg}; use std::time::Duration as StdDuration; /// The number of nanoseconds in a microsecond. @@ -131,14 +131,6 @@ impl Duration { Duration { secs: secs, nanos: nanos as i32 } } - /// Runs a closure, returning the duration of time it took to run the - /// closure. - pub fn span(f: F) -> Duration where F: FnOnce() { - let before = super::precise_time_ns(); - f(); - Duration::nanoseconds((super::precise_time_ns() - before) as i64) - } - /// Returns the total number of whole weeks in the duration. #[inline] pub fn num_weeks(&self) -> i64 { From 5e68c60bb2c5cde7e9432d10b0bebb211b712b24 Mon Sep 17 00:00:00 2001 From: Jethro Beekman Date: Fri, 24 Feb 2017 15:20:07 -0800 Subject: [PATCH 4/4] Add clock feature --- .travis.sh | 11 +++++++++++ Cargo.toml | 6 +++++- src/datetime.rs | 37 ++++++++++++++++++++++++++++++------- src/lib.rs | 11 +++++++++-- src/offset/mod.rs | 3 ++- src/offset/utc.rs | 3 +++ 6 files changed, 60 insertions(+), 11 deletions(-) diff --git a/.travis.sh b/.travis.sh index 872a5b19a1..2aeab9bc1e 100755 --- a/.travis.sh +++ b/.travis.sh @@ -37,6 +37,16 @@ build_and_test() { TZ=UTC0 channel test -v --features serde --lib channel build -v --features serde,rustc-serialize TZ=Asia/Katmandu channel test -v --features serde,rustc-serialize + + # without default "clock" feature + channel build -v --no-default-features + TZ=ACST-9:30 channel test -v --no-default-features --lib + channel build -v --no-default-features --features rustc-serialize + TZ=EST4 channel test -v --no-default-features --features rustc-serialize --lib + channel build -v --no-default-features --features serde + TZ=UTC0 channel test -v --no-default-features --features serde --lib + channel build -v --no-default-features --features serde,rustc-serialize + TZ=Asia/Katmandu channel test -v --no-default-features --features serde,rustc-serialize --lib } build_only() { @@ -46,6 +56,7 @@ build_only() { channel build -v channel build -v --features rustc-serialize channel build -v --features 'serde bincode' + channel build -v --no-default-features } run_clippy() { diff --git a/Cargo.toml b/Cargo.toml index 731f11bd6b..99e492632e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,8 +19,12 @@ appveyor = { repository = "chronotope/chrono" } [lib] name = "chrono" +[features] +default = ["clock"] +clock = ["time"] + [dependencies] -time = "^0.1.36" +time = { version = "^0.1.36", optional = true } num-integer = { version = "0.1.36", default-features = false } num-traits = { version = "0.2", default-features = false } rustc-serialize = { version = "0.3", optional = true } diff --git a/src/datetime.rs b/src/datetime.rs index ec5e85aced..3033485b01 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -10,7 +10,9 @@ use std::time::{SystemTime, UNIX_EPOCH}; use oldtime::Duration as OldDuration; use {Weekday, Timelike, Datelike}; -use offset::{TimeZone, Offset, Utc, Local, FixedOffset}; +#[cfg(feature="clock")] +use offset::Local; +use offset::{TimeZone, Offset, Utc, FixedOffset}; use naive::{NaiveTime, NaiveDateTime, IsoWeek}; use Date; use format::{Item, Numeric, Pad, Fixed}; @@ -532,6 +534,7 @@ impl str::FromStr for DateTime { } } +#[cfg(feature="clock")] impl str::FromStr for DateTime { type Err = ParseError; @@ -558,6 +561,7 @@ impl From for DateTime { } } +#[cfg(feature="clock")] impl From for DateTime { fn from(t: SystemTime) -> DateTime { DateTime::::from(t).with_timezone(&Local) @@ -594,7 +598,7 @@ fn test_encodable_json(to_string_utc: FUtc, to_string_fixed: FF Some(r#""2014-07-24T12:34:06+01:00:50""#.into())); } -#[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))] +#[cfg(all(test, feature="clock", any(feature = "rustc-serialize", feature = "serde")))] fn test_decodable_json(utc_from_str: FUtc, fixed_from_str: FFixed, local_from_str: FLocal) @@ -631,7 +635,7 @@ fn test_decodable_json(utc_from_str: FUtc, assert!(fixed_from_str(r#""2014-07-32T12:34:06Z""#).is_err()); } -#[cfg(all(test, feature = "rustc-serialize"))] +#[cfg(all(test, feature="clock", feature = "rustc-serialize"))] fn test_decodable_json_timestamps(utc_from_str: FUtc, fixed_from_str: FFixed, local_from_str: FLocal) @@ -665,7 +669,9 @@ pub mod rustc_serialize { use std::fmt; use std::ops::Deref; use super::DateTime; - use offset::{TimeZone, LocalResult, Utc, Local, FixedOffset}; + #[cfg(feature="clock")] + use offset::Local; + use offset::{TimeZone, LocalResult, Utc, FixedOffset}; use rustc_serialize::{Encodable, Encoder, Decodable, Decoder}; impl Encodable for DateTime { @@ -739,6 +745,7 @@ pub mod rustc_serialize { } } + #[cfg(feature="clock")] impl Decodable for DateTime { fn decode(d: &mut D) -> Result, D::Error> { match d.read_str()?.parse::>() { @@ -748,6 +755,7 @@ pub mod rustc_serialize { } } + #[cfg(feature="clock")] impl Decodable for TsSeconds { fn decode(d: &mut D) -> Result, D::Error> { from(Utc.timestamp_opt(d.read_i64()?, 0), d) @@ -762,11 +770,13 @@ pub mod rustc_serialize { super::test_encodable_json(json::encode, json::encode); } + #[cfg(feature="clock")] #[test] fn test_decodable() { super::test_decodable_json(json::decode, json::decode, json::decode); } + #[cfg(feature="clock")] #[test] fn test_decodable_timestamps() { super::test_decodable_json_timestamps(json::decode, json::decode, json::decode); @@ -779,7 +789,9 @@ pub mod rustc_serialize { pub mod serde { use std::fmt; use super::DateTime; - use offset::{TimeZone, Utc, Local, FixedOffset}; + #[cfg(feature="clock")] + use offset::Local; + use offset::{TimeZone, Utc, FixedOffset}; use serdelib::{ser, de}; /// Ser/de to/from timestamps in seconds @@ -1015,6 +1027,7 @@ pub mod serde { /// /// See [the `serde` module](./serde/index.html) for alternate /// serialization formats. + #[cfg(feature="clock")] impl<'de> de::Deserialize<'de> for DateTime { fn deserialize(deserializer: D) -> Result where D: de::Deserializer<'de> @@ -1031,6 +1044,7 @@ pub mod serde { super::test_encodable_json(self::serde_json::to_string, self::serde_json::to_string); } + #[cfg(feature="clock")] #[test] fn test_serde_deserialize() { super::test_decodable_json(|input| self::serde_json::from_str(&input), |input| self::serde_json::from_str(&input), @@ -1054,9 +1068,12 @@ pub mod serde { #[cfg(test)] mod tests { use super::DateTime; + #[cfg(feature="clock")] use Datelike; use naive::{NaiveTime, NaiveDate}; - use offset::{TimeZone, Utc, Local, FixedOffset}; + #[cfg(feature="clock")] + use offset::Local; + use offset::{TimeZone, Utc, FixedOffset}; use oldtime::Duration; use std::time::{SystemTime, UNIX_EPOCH}; @@ -1130,6 +1147,7 @@ mod tests { } #[test] + #[cfg(feature="clock")] fn test_datetime_with_timezone() { let local_now = Local::now(); let utc_now = local_now.with_timezone(&Utc); @@ -1225,6 +1243,7 @@ mod tests { } #[test] + #[cfg(feature="clock")] fn test_datetime_format_with_local() { // if we are not around the year boundary, local and UTC date should have the same year let dt = Local::now().with_month(5).unwrap(); @@ -1232,6 +1251,7 @@ mod tests { } #[test] + #[cfg(feature="clock")] fn test_datetime_is_copy() { // UTC is known to be `Copy`. let a = Utc::now(); @@ -1240,6 +1260,7 @@ mod tests { } #[test] + #[cfg(feature="clock")] fn test_datetime_is_send() { use std::thread; @@ -1280,7 +1301,9 @@ mod tests { UNIX_EPOCH - Duration::new(999_999_999, 999_999_999)); // DateTime -> SystemTime (via `with_timezone`) - assert_eq!(SystemTime::from(epoch.with_timezone(&Local)), UNIX_EPOCH); + #[cfg(feature="clock")] { + assert_eq!(SystemTime::from(epoch.with_timezone(&Local)), UNIX_EPOCH); + } assert_eq!(SystemTime::from(epoch.with_timezone(&FixedOffset::east(32400))), UNIX_EPOCH); assert_eq!(SystemTime::from(epoch.with_timezone(&FixedOffset::west(28800))), UNIX_EPOCH); } diff --git a/src/lib.rs b/src/lib.rs index 6aad9ea2af..ce202bf16d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -396,6 +396,7 @@ // field-init-shorthand, which was stabilized in rust 1.17. #![cfg_attr(feature = "cargo-clippy", allow(const_static_lifetime, redundant_field_names))] +#[cfg(feature="clock")] extern crate time as oldtime; extern crate num_integer; extern crate num_traits; @@ -407,7 +408,9 @@ extern crate serde as serdelib; // this reexport is to aid the transition and should not be in the prelude! pub use oldtime::Duration; -#[doc(no_inline)] pub use offset::{TimeZone, Offset, LocalResult, Utc, FixedOffset, Local}; +#[cfg(feature="clock")] +#[doc(no_inline)] pub use offset::Local; +#[doc(no_inline)] pub use offset::{TimeZone, Offset, LocalResult, Utc, FixedOffset}; #[doc(no_inline)] pub use naive::{NaiveDate, IsoWeek, NaiveTime, NaiveDateTime}; pub use date::{Date, MIN_DATE, MAX_DATE}; pub use datetime::{DateTime, SecondsFormat}; @@ -419,7 +422,9 @@ pub use round::SubsecRound; pub mod prelude { #[doc(no_inline)] pub use {Datelike, Timelike, Weekday}; #[doc(no_inline)] pub use {TimeZone, Offset}; - #[doc(no_inline)] pub use {Utc, FixedOffset, Local}; + #[cfg(feature="clock")] + #[doc(no_inline)] pub use Local; + #[doc(no_inline)] pub use {Utc, FixedOffset}; #[doc(no_inline)] pub use {NaiveDate, NaiveTime, NaiveDateTime}; #[doc(no_inline)] pub use Date; #[doc(no_inline)] pub use {DateTime, SecondsFormat}; @@ -432,6 +437,8 @@ macro_rules! try_opt { } mod div; +#[cfg(not(feature="clock"))] +mod oldtime; pub mod offset; pub mod naive { //! Date and time types which do not concern about the timezones. diff --git a/src/offset/mod.rs b/src/offset/mod.rs index f49a688f2a..e94ab503fa 100644 --- a/src/offset/mod.rs +++ b/src/offset/mod.rs @@ -371,9 +371,10 @@ pub trait TimeZone: Sized + Clone { mod utc; mod fixed; +#[cfg(feature="clock")] mod local; pub use self::utc::Utc; pub use self::fixed::FixedOffset; +#[cfg(feature="clock")] pub use self::local::Local; - diff --git a/src/offset/utc.rs b/src/offset/utc.rs index ffdd53e42b..d4e8d10b5a 100644 --- a/src/offset/utc.rs +++ b/src/offset/utc.rs @@ -4,9 +4,11 @@ //! The UTC (Coordinated Universal Time) time zone. use std::fmt; +#[cfg(feature="clock")] use oldtime; use naive::{NaiveDate, NaiveDateTime}; +#[cfg(feature="clock")] use {Date, DateTime}; use super::{TimeZone, Offset, LocalResult, FixedOffset}; @@ -30,6 +32,7 @@ use super::{TimeZone, Offset, LocalResult, FixedOffset}; #[derive(Copy, Clone, PartialEq, Eq)] pub struct Utc; +#[cfg(feature="clock")] impl Utc { /// Returns a `Date` which corresponds to the current date. pub fn today() -> Date { Utc::now().date() }