// This is a part of rust-chrono. // Copyright (c) 2014, Kang Seonghoon. // See README.md and LICENSE.txt for details. /*! * ISO 8601 duration. */ use std::{fmt, num, i32}; use num::Integer; /// `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, Clone)] 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 { let (secs_, nanos) = nanos.div_mod_floor(&NANOS_PER_SEC); let secs = try_opt!(secs.checked_add(&secs_)); let (days_, secs) = secs.div_mod_floor(&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 zero seconds. #[inline] pub fn zero() -> Duration { Duration { days: 0, secs: 0, nanos: 0 } } /// 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) = hours.div_mod_floor(&(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) = mins.div_mod_floor(&(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) = secs.div_mod_floor(&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) = millis.div_mod_floor(&(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) = micros.div_mod_floor(&(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) = nanos.div_mod_floor(&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 { fn micros((days, secs, nanos): (i64, u32, u32)) -> Option { 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 { fn nanos((days, secs, nanos): (i64, u32, u32)) -> Option { 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 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 } // XXX can overflow } } impl Add 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 { 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 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 { 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 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) = x.div_mod_floor(&limit); let (h, l) = (xh * y, xl * y); let (h_, l) = l.div_rem(&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) = secs1.div_mod_floor(&(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 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) = days.div_mod_floor(&rhs); let secs = secs + carry * SECS_PER_DAY as i64; let (secs, carry) = secs.div_mod_floor(&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(()) } } #[cfg(test)] mod tests { use super::{Duration, MIN_DAYS, MAX_DAYS, MIN, MAX}; use std::{i32, i64}; #[test] fn test_duration() { assert_eq!(Duration::zero(), Duration::zero()); assert!(Duration::zero() != Duration::seconds(1)); 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::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() { 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); 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() { 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::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() { 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::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() { 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::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() { 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)); } #[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)); } #[test] fn test_duration_fmt() { assert_eq!(Duration::zero().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()); } }