diff options
| author | Brian Anderson <banderson@mozilla.com> | 2014-07-31 17:24:54 -0700 |
|---|---|---|
| committer | Brian Anderson <banderson@mozilla.com> | 2014-08-13 11:31:48 -0700 |
| commit | 77cdaf018c98c1a89afcf8382c3f3e90907f49a7 (patch) | |
| tree | ff8def4d1ca544d550c94cd4939f7ec57cc70551 /src/libstd/time | |
| parent | 4475e6a095a8fe4459ac8b854ae2336e47aaafe5 (diff) | |
| download | rust-77cdaf018c98c1a89afcf8382c3f3e90907f49a7.tar.gz rust-77cdaf018c98c1a89afcf8382c3f3e90907f49a7.zip | |
std: Refactor time module a bit
Put `Duration` in `time::duration`, where the two constants can be called just `MAX` and `MIN`. Reexport from `time`. This provides more room for the time module to expand.
Diffstat (limited to 'src/libstd/time')
| -rw-r--r-- | src/libstd/time/duration.rs | 681 | ||||
| -rw-r--r-- | src/libstd/time/mod.rs | 15 |
2 files changed, 696 insertions, 0 deletions
diff --git a/src/libstd/time/duration.rs b/src/libstd/time/duration.rs new file mode 100644 index 00000000000..7f31df96b07 --- /dev/null +++ b/src/libstd/time/duration.rs @@ -0,0 +1,681 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Temporal quantification + +#![experimental] + +use {fmt, i32}; +use ops::{Add, Sub, Mul, Div, Neg}; +use option::{Option, Some, None}; +use num; +use num::{CheckedAdd, CheckedMul, ToPrimitive}; +use result::{Result, Ok, Err}; + + +/// `Duration`'s `days` component should have no more than this value. +static MIN_DAYS: i32 = i32::MIN; +/// `Duration`'s `days` component should have no less than this value. +static MAX_DAYS: i32 = i32::MAX; + +/// The number of nanoseconds in seconds. +static NANOS_PER_SEC: i32 = 1_000_000_000; +/// The number of (non-leap) seconds in days. +static SECS_PER_DAY: i32 = 86400; + +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. +#[deriving(PartialEq, Eq, PartialOrd, Ord)] +pub struct Duration { + days: i32, + secs: u32, + nanos: u32, +} + +/// The minimum possible `Duration`. +pub static MIN: Duration = Duration { days: MIN_DAYS, secs: 0, nanos: 0 }; +/// The maximum possible `Duration`. +pub static MAX: Duration = Duration { days: MAX_DAYS, secs: SECS_PER_DAY as u32 - 1, + nanos: NANOS_PER_SEC as u32 - 1 }; + +impl Duration { + /// Makes a new `Duration` with given number of days, seconds and nanoseconds. + /// + /// Fails when the duration is out of bounds. + #[inline] + pub fn new(days: i32, secs: i32, nanos: i32) -> Duration { + Duration::new_opt(days, secs, nanos).expect("Duration::new out of bounds") + } + + /// Makes a new `Duration` with given number of days, seconds and nanoseconds. + /// + /// Returns `None` when the duration is out of bounds. + pub fn new_opt(days: i32, secs: i32, nanos: i32) -> Option<Duration> { + let (secs_, nanos) = div_mod_floor(nanos, NANOS_PER_SEC); + let secs = try_opt!(secs.checked_add(&secs_)); + let (days_, secs) = div_mod_floor(secs, SECS_PER_DAY); + let days = try_opt!(days.checked_add(&days_).and_then(|v| v.to_i32())); + Some(Duration { days: days, secs: secs as u32, nanos: nanos as u32 }) + } + + /// Makes a new `Duration` with given number of weeks. + /// Equivalent to `Duration::new(weeks * 7, 0, 0)` with overflow checks. + /// + /// Fails when the duration is out of bounds. + #[inline] + pub fn weeks(weeks: i32) -> Duration { + let days = weeks.checked_mul(&7).expect("Duration::weeks out of bounds"); + Duration::days(days) + } + + /// Makes a new `Duration` with given number of days. + /// Equivalent to `Duration::new(days, 0, 0)`. + /// + /// Fails when the duration is out of bounds. + #[inline] + pub fn days(days: i32) -> Duration { + let days = days.to_i32().expect("Duration::days out of bounds"); + Duration { days: days, secs: 0, nanos: 0 } + } + + /// Makes a new `Duration` with given number of hours. + /// Equivalent to `Duration::new(0, hours * 3600, 0)` with overflow checks. + /// + /// Fails when the duration is out of bounds. + #[inline] + pub fn hours(hours: i32) -> Duration { + let (days, hours) = div_mod_floor(hours, (SECS_PER_DAY / 3600)); + let secs = hours * 3600; + Duration { secs: secs as u32, ..Duration::days(days) } + } + + /// Makes a new `Duration` with given number of minutes. + /// Equivalent to `Duration::new(0, mins * 60, 0)` with overflow checks. + /// + /// Fails when the duration is out of bounds. + #[inline] + pub fn minutes(mins: i32) -> Duration { + let (days, mins) = div_mod_floor(mins, (SECS_PER_DAY / 60)); + let secs = mins * 60; + Duration { secs: secs as u32, ..Duration::days(days) } + } + + /// Makes a new `Duration` with given number of seconds. + /// Equivalent to `Duration::new(0, secs, 0)`. + /// + /// Fails when the duration is out of bounds. + #[inline] + pub fn seconds(secs: i32) -> Duration { + let (days, secs) = div_mod_floor(secs, SECS_PER_DAY); + Duration { secs: secs as u32, ..Duration::days(days) } + } + + /// Makes a new `Duration` with given number of milliseconds. + /// Equivalent to `Duration::new(0, 0, millis * 1_000_000)` with overflow checks. + /// + /// Fails when the duration is out of bounds. + #[inline] + pub fn milliseconds(millis: i32) -> Duration { + let (secs, millis) = div_mod_floor(millis, (NANOS_PER_SEC / 1_000_000)); + let nanos = millis * 1_000_000; + Duration { nanos: nanos as u32, ..Duration::seconds(secs) } + } + + /// Makes a new `Duration` with given number of microseconds. + /// Equivalent to `Duration::new(0, 0, micros * 1_000)` with overflow checks. + /// + /// Fails when the duration is out of bounds. + #[inline] + pub fn microseconds(micros: i32) -> Duration { + let (secs, micros) = div_mod_floor(micros, (NANOS_PER_SEC / 1_000)); + let nanos = micros * 1_000; + Duration { nanos: nanos as u32, ..Duration::seconds(secs) } + } + + /// Makes a new `Duration` with given number of nanoseconds. + /// Equivalent to `Duration::new(0, 0, nanos)`. + /// + /// Fails when the duration is out of bounds. + #[inline] + pub fn nanoseconds(nanos: i32) -> Duration { + let (secs, nanos) = div_mod_floor(nanos, NANOS_PER_SEC); + Duration { nanos: nanos as u32, ..Duration::seconds(secs) } + } + + /// Returns a tuple of the number of days, (non-leap) seconds and nanoseconds in the duration. + /// Note that the number of seconds and nanoseconds are always positive, + /// so that for example `-Duration::seconds(3)` has -1 days and 86,397 seconds. + #[inline] + pub fn to_tuple(&self) -> (i32, u32, u32) { + (self.days, self.secs, self.nanos) + } + + /// Same as `to_tuple` but returns a tuple compatible to `to_negated_tuple`. + #[inline] + fn to_tuple_64(&self) -> (i64, u32, u32) { + (self.days as i64, self.secs, self.nanos) + } + + /// Negates the duration and returns a tuple like `to_tuple`. + /// This does not overflow and thus is internally used for several methods. + fn to_negated_tuple_64(&self) -> (i64, u32, u32) { + let mut days = -(self.days as i64); + let mut secs = -(self.secs as i32); + let mut nanos = -(self.nanos as i32); + if nanos < 0 { + nanos += NANOS_PER_SEC; + secs -= 1; + } + if secs < 0 { + secs += SECS_PER_DAY; + days -= 1; + } + (days, secs as u32, nanos as u32) + } + + /// Returns the total number of whole weeks in the duration. + #[inline] + pub fn num_weeks(&self) -> i32 { + self.num_days() / 7 + } + + /// Returns the total number of whole days in the duration. + pub fn num_days(&self) -> i32 { + if self.days < 0 { + let negated = -*self; + -negated.days + } else { + self.days + } + } + + /// Returns the total number of whole hours in the duration. + #[inline] + pub fn num_hours(&self) -> i64 { + self.num_seconds() / 3600 + } + + /// Returns the total number of whole minutes in the duration. + #[inline] + pub fn num_minutes(&self) -> i64 { + self.num_seconds() / 60 + } + + /// Returns the total number of (non-leap) whole seconds in the duration. + pub fn num_seconds(&self) -> i64 { + // cannot overflow, 2^32 * 86400 < 2^64 + fn secs((days, secs, _): (i64, u32, u32)) -> i64 { + days as i64 * SECS_PER_DAY as i64 + secs as i64 + } + if self.days < 0 {-secs(self.to_negated_tuple_64())} else {secs(self.to_tuple_64())} + } + + /// Returns the total number of whole milliseconds in the duration. + pub fn num_milliseconds(&self) -> i64 { + // cannot overflow, 2^32 * 86400 * 1000 < 2^64 + fn millis((days, secs, nanos): (i64, u32, u32)) -> i64 { + static MILLIS_PER_SEC: i64 = 1_000; + static NANOS_PER_MILLI: i64 = 1_000_000; + (days as i64 * MILLIS_PER_SEC * SECS_PER_DAY as i64 + + secs as i64 * MILLIS_PER_SEC + + nanos as i64 / NANOS_PER_MILLI) + } + if self.days < 0 {-millis(self.to_negated_tuple_64())} else {millis(self.to_tuple_64())} + } + + /// Returns the total number of whole microseconds in the duration, + /// or `None` on the overflow (exceeding 2^63 microseconds in either directions). + pub fn num_microseconds(&self) -> Option<i64> { + fn micros((days, secs, nanos): (i64, u32, u32)) -> Option<i64> { + static MICROS_PER_SEC: i64 = 1_000_000; + static MICROS_PER_DAY: i64 = MICROS_PER_SEC * SECS_PER_DAY as i64; + static NANOS_PER_MICRO: i64 = 1_000; + let nmicros = try_opt!((days as i64).checked_mul(&MICROS_PER_DAY)); + let nmicros = try_opt!(nmicros.checked_add(&(secs as i64 * MICROS_PER_SEC))); + let nmicros = try_opt!(nmicros.checked_add(&(nanos as i64 / NANOS_PER_MICRO as i64))); + Some(nmicros) + } + if self.days < 0 { + // the final negation won't overflow since we start with positive numbers. + micros(self.to_negated_tuple_64()).map(|micros| -micros) + } else { + micros(self.to_tuple_64()) + } + } + + /// Returns the total number of whole nanoseconds in the duration, + /// or `None` on the overflow (exceeding 2^63 nanoseconds in either directions). + pub fn num_nanoseconds(&self) -> Option<i64> { + fn nanos((days, secs, nanos): (i64, u32, u32)) -> Option<i64> { + static NANOS_PER_DAY: i64 = NANOS_PER_SEC as i64 * SECS_PER_DAY as i64; + let nnanos = try_opt!((days as i64).checked_mul(&NANOS_PER_DAY)); + let nnanos = try_opt!(nnanos.checked_add(&(secs as i64 * NANOS_PER_SEC as i64))); + let nnanos = try_opt!(nnanos.checked_add(&(nanos as i64))); + Some(nnanos) + } + if self.days < 0 { + // the final negation won't overflow since we start with positive numbers. + nanos(self.to_negated_tuple_64()).map(|micros| -micros) + } else { + nanos(self.to_tuple_64()) + } + } +} + +impl num::Bounded for Duration { + #[inline] fn min_value() -> Duration { MIN } + #[inline] fn max_value() -> Duration { MAX } +} + +impl num::Zero for Duration { + #[inline] + fn zero() -> Duration { + Duration { days: 0, secs: 0, nanos: 0 } + } + + #[inline] + fn is_zero(&self) -> bool { + self.days == 0 && self.secs == 0 && self.nanos == 0 + } +} + +impl Neg<Duration> for Duration { + #[inline] + fn neg(&self) -> Duration { + let (days, secs, nanos) = self.to_negated_tuple_64(); + Duration { days: days as i32, secs: secs, nanos: nanos } // FIXME can overflow + } +} + +impl Add<Duration,Duration> for Duration { + fn add(&self, rhs: &Duration) -> Duration { + let mut days = self.days + rhs.days; + let mut secs = self.secs + rhs.secs; + let mut nanos = self.nanos + rhs.nanos; + if nanos >= NANOS_PER_SEC as u32 { + nanos -= NANOS_PER_SEC as u32; + secs += 1; + } + if secs >= SECS_PER_DAY as u32 { + secs -= SECS_PER_DAY as u32; + days += 1; + } + Duration { days: days, secs: secs, nanos: nanos } + } +} + +impl num::CheckedAdd for Duration { + fn checked_add(&self, rhs: &Duration) -> Option<Duration> { + let mut days = try_opt!(self.days.checked_add(&rhs.days)); + let mut secs = self.secs + rhs.secs; + let mut nanos = self.nanos + rhs.nanos; + if nanos >= NANOS_PER_SEC as u32 { + nanos -= NANOS_PER_SEC as u32; + secs += 1; + } + if secs >= SECS_PER_DAY as u32 { + secs -= SECS_PER_DAY as u32; + days = try_opt!(days.checked_add(&1)); + } + Some(Duration { days: days, secs: secs, nanos: nanos }) + } +} + +impl Sub<Duration,Duration> for Duration { + fn sub(&self, rhs: &Duration) -> Duration { + let mut days = self.days - rhs.days; + let mut secs = self.secs as i32 - rhs.secs as i32; + let mut nanos = self.nanos as i32 - rhs.nanos as i32; + if nanos < 0 { + nanos += NANOS_PER_SEC; + secs -= 1; + } + if secs < 0 { + secs += SECS_PER_DAY; + days -= 1; + } + Duration { days: days, secs: secs as u32, nanos: nanos as u32 } + } +} + +impl num::CheckedSub for Duration { + fn checked_sub(&self, rhs: &Duration) -> Option<Duration> { + let mut days = try_opt!(self.days.checked_sub(&rhs.days)); + let mut secs = self.secs as i32 - rhs.secs as i32; + let mut nanos = self.nanos as i32 - rhs.nanos as i32; + if nanos < 0 { + nanos += NANOS_PER_SEC; + secs -= 1; + } + if secs < 0 { + secs += SECS_PER_DAY; + days = try_opt!(days.checked_sub(&1)); + } + Some(Duration { days: days, secs: secs as u32, nanos: nanos as u32 }) + } +} + +impl Mul<i32,Duration> for Duration { + fn mul(&self, rhs: &i32) -> Duration { + /// Given `0 <= y < limit <= 2^30`, + /// returns `(h,l)` such that `x * y = h * limit + l` where `0 <= l < limit`. + fn mul_i64_u32_limit(x: i64, y: u32, limit: u32) -> (i64,u32) { + let y = y as i64; + let limit = limit as i64; + let (xh, xl) = div_mod_floor_64(x, limit); + let (h, l) = (xh * y, xl * y); + let (h_, l) = div_rem_64(l, limit); + (h + h_, l as u32) + } + + let rhs = *rhs as i64; + let (secs1, nanos) = mul_i64_u32_limit(rhs, self.nanos, NANOS_PER_SEC as u32); + let (days1, secs1) = div_mod_floor_64(secs1, (SECS_PER_DAY as i64)); + let (days2, secs2) = mul_i64_u32_limit(rhs, self.secs, SECS_PER_DAY as u32); + let mut days = self.days as i64 * rhs + days1 + days2; + let mut secs = secs1 as u32 + secs2; + if secs >= SECS_PER_DAY as u32 { + secs -= 1; + days += 1; + } + Duration { days: days as i32, secs: secs, nanos: nanos } + } +} + +impl Div<i32,Duration> for Duration { + fn div(&self, rhs: &i32) -> Duration { + let (rhs, days, secs, nanos) = if *rhs < 0 { + let (days, secs, nanos) = self.to_negated_tuple_64(); + (-(*rhs as i64), days, secs as i64, nanos as i64) + } else { + (*rhs as i64, self.days as i64, self.secs as i64, self.nanos as i64) + }; + + let (days, carry) = div_mod_floor_64(days, rhs); + let secs = secs + carry * SECS_PER_DAY as i64; + let (secs, carry) = div_mod_floor_64(secs, rhs); + let nanos = nanos + carry * NANOS_PER_SEC as i64; + let nanos = nanos / rhs; + Duration { days: days as i32, secs: secs as u32, nanos: nanos as u32 } + } +} + +impl fmt::Show for Duration { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let hasdate = self.days != 0; + let hastime = (self.secs != 0 || self.nanos != 0) || !hasdate; + + try!(write!(f, "P")); + if hasdate { + // technically speaking the negative part is not the valid ISO 8601, + // but we need to print it anyway. + try!(write!(f, "{}D", self.days)); + } + if hastime { + if self.nanos == 0 { + try!(write!(f, "T{}S", self.secs)); + } else if self.nanos % 1_000_000 == 0 { + try!(write!(f, "T{}.{:03}S", self.secs, self.nanos / 1_000_000)); + } else if self.nanos % 1_000 == 0 { + try!(write!(f, "T{}.{:06}S", self.secs, self.nanos / 1_000)); + } else { + try!(write!(f, "T{}.{:09}S", self.secs, self.nanos)); + } + } + Ok(()) + } +} + +// Copied from libnum +#[inline] +fn div_mod_floor(this: i32, other: i32) -> (i32, i32) { + (div_floor(this, other), mod_floor(this, other)) +} + +#[inline] +fn div_floor(this: i32, other: i32) -> i32 { + match div_rem(this, other) { + (d, r) if (r > 0 && other < 0) + || (r < 0 && other > 0) => d - 1, + (d, _) => d, + } +} + +#[inline] +fn mod_floor(this: i32, other: i32) -> i32 { + match this % other { + r if (r > 0 && other < 0) + || (r < 0 && other > 0) => r + other, + r => r, + } +} + +#[inline] +fn div_rem(this: i32, other: i32) -> (i32, i32) { + (this / other, this % other) +} + +#[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_DAYS, MAX_DAYS, MIN, MAX}; + use {i32, i64}; + use num::{Zero, CheckedAdd, CheckedSub}; + use option::{Some, None}; + use to_string::ToString; + + #[test] + fn test_duration() { + let d: Duration = Zero::zero(); + assert_eq!(d, Zero::zero()); + assert!(Duration::seconds(1) != Zero::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() { + let d: Duration = Zero::zero(); + assert_eq!(d.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::new(1, 2, 3_004_005).num_days(), 1); + assert_eq!(Duration::new(-1, -2, -3_004_005).num_days(), -1); + assert_eq!(Duration::days(i32::MAX).num_days(), i32::MAX); + assert_eq!(Duration::days(i32::MIN).num_days(), i32::MIN); + assert_eq!(MAX.num_days(), MAX_DAYS); + assert_eq!(MIN.num_days(), MIN_DAYS); + } + + #[test] + fn test_duration_num_seconds() { + let d: Duration = Zero::zero(); + assert_eq!(d.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); + assert_eq!(Duration::new(1, 2, 3_004_005).num_seconds(), 86402); + assert_eq!(Duration::new(-1, -2, -3_004_005).num_seconds(), -86402); + assert_eq!(Duration::seconds(i32::MAX).num_seconds(), i32::MAX as i64); + assert_eq!(Duration::seconds(i32::MIN).num_seconds(), i32::MIN as i64); + assert_eq!(MAX.num_seconds(), (MAX_DAYS as i64 + 1) * 86400 - 1); + assert_eq!(MIN.num_seconds(), MIN_DAYS as i64 * 86400); + } + + #[test] + fn test_duration_num_milliseconds() { + let d: Duration = Zero::zero(); + assert_eq!(d.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::new(1, 2, 3_004_005).num_milliseconds(), 86402_003); + assert_eq!(Duration::new(-1, -2, -3_004_005).num_milliseconds(), -86402_003); + assert_eq!(Duration::milliseconds(i32::MAX).num_milliseconds(), i32::MAX as i64); + assert_eq!(Duration::milliseconds(i32::MIN).num_milliseconds(), i32::MIN as i64); + assert_eq!(MAX.num_milliseconds(), (MAX_DAYS as i64 + 1) * 86400_000 - 1); + assert_eq!(MIN.num_milliseconds(), MIN_DAYS as i64 * 86400_000); + } + + #[test] + fn test_duration_num_microseconds() { + let d: Duration = Zero::zero(); + assert_eq!(d.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::new(1, 2, 3_004_005).num_microseconds(), Some(86402_003_004)); + assert_eq!(Duration::new(-1, -2, -3_004_005).num_microseconds(), Some(-86402_003_004)); + assert_eq!(Duration::microseconds(i32::MAX).num_microseconds(), Some(i32::MAX as i64)); + assert_eq!(Duration::microseconds(i32::MIN).num_microseconds(), Some(i32::MIN as i64)); + assert_eq!(MAX.num_microseconds(), None); + assert_eq!(MIN.num_microseconds(), None); + + // overflow checks + static MICROS_PER_DAY: i64 = 86400_000_000; + assert_eq!(Duration::days((i64::MAX / MICROS_PER_DAY) as i32).num_microseconds(), + Some(i64::MAX / MICROS_PER_DAY * MICROS_PER_DAY)); + assert_eq!(Duration::days((i64::MIN / MICROS_PER_DAY) as i32).num_microseconds(), + Some(i64::MIN / MICROS_PER_DAY * MICROS_PER_DAY)); + assert_eq!(Duration::days((i64::MAX / MICROS_PER_DAY + 1) as i32).num_microseconds(), None); + assert_eq!(Duration::days((i64::MIN / MICROS_PER_DAY - 1) as i32).num_microseconds(), None); + } + + #[test] + fn test_duration_num_nanoseconds() { + let d: Duration = Zero::zero(); + assert_eq!(d.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::new(1, 2, 3_004_005).num_nanoseconds(), Some(86402_003_004_005)); + assert_eq!(Duration::new(-1, -2, -3_004_005).num_nanoseconds(), Some(-86402_003_004_005)); + assert_eq!(Duration::nanoseconds(i32::MAX).num_nanoseconds(), Some(i32::MAX as i64)); + assert_eq!(Duration::nanoseconds(i32::MIN).num_nanoseconds(), Some(i32::MIN as i64)); + assert_eq!(MAX.num_nanoseconds(), None); + assert_eq!(MIN.num_nanoseconds(), None); + + // overflow checks + static NANOS_PER_DAY: i64 = 86400_000_000_000; + assert_eq!(Duration::days((i64::MAX / NANOS_PER_DAY) as i32).num_nanoseconds(), + Some(i64::MAX / NANOS_PER_DAY * NANOS_PER_DAY)); + assert_eq!(Duration::days((i64::MIN / NANOS_PER_DAY) as i32).num_nanoseconds(), + Some(i64::MIN / NANOS_PER_DAY * NANOS_PER_DAY)); + assert_eq!(Duration::days((i64::MAX / NANOS_PER_DAY + 1) as i32).num_nanoseconds(), None); + assert_eq!(Duration::days((i64::MIN / NANOS_PER_DAY - 1) as i32).num_nanoseconds(), None); + } + + #[test] + fn test_duration_checked_ops() { + assert_eq!(Duration::days(MAX_DAYS).checked_add(&Duration::seconds(86399)), + Some(Duration::days(MAX_DAYS - 1) + Duration::seconds(86400+86399))); + assert!(Duration::days(MAX_DAYS).checked_add(&Duration::seconds(86400)).is_none()); + + assert_eq!(Duration::days(MIN_DAYS).checked_sub(&Duration::seconds(0)), + Some(Duration::days(MIN_DAYS))); + assert!(Duration::days(MIN_DAYS).checked_sub(&Duration::seconds(1)).is_none()); + } + + #[test] + fn test_duration_mul() { + let d: Duration = Zero::zero(); + assert_eq!(d * i32::MAX, d); + assert_eq!(d * i32::MIN, d); + assert_eq!(Duration::nanoseconds(1) * 0, Zero::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)); + } + + #[test] + fn test_duration_div() { + let d: Duration = Zero::zero(); + assert_eq!(d / i32::MAX, d); + assert_eq!(d / i32::MIN, d); + 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)); + } + + #[test] + fn test_duration_fmt() { + let d: Duration = Zero::zero(); + assert_eq!(d.to_string(), "PT0S".to_string()); + assert_eq!(Duration::days(42).to_string(), "P42D".to_string()); + assert_eq!(Duration::days(-42).to_string(), "P-42D".to_string()); + assert_eq!(Duration::seconds(42).to_string(), "PT42S".to_string()); + assert_eq!(Duration::milliseconds(42).to_string(), "PT0.042S".to_string()); + assert_eq!(Duration::microseconds(42).to_string(), "PT0.000042S".to_string()); + assert_eq!(Duration::nanoseconds(42).to_string(), "PT0.000000042S".to_string()); + assert_eq!((Duration::days(7) + Duration::milliseconds(6543)).to_string(), + "P7DT6.543S".to_string()); + + // the format specifier should have no effect on `Duration` + assert_eq!(format!("{:30}", Duration::days(1) + Duration::milliseconds(2345)), + "P1DT2.345S".to_string()); + } +} diff --git a/src/libstd/time/mod.rs b/src/libstd/time/mod.rs new file mode 100644 index 00000000000..436fa5ebdea --- /dev/null +++ b/src/libstd/time/mod.rs @@ -0,0 +1,15 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Temporal quantification. + +pub use self::duration::Duration; + +pub mod duration; |
