diff options
Diffstat (limited to 'vendor/time/src/duration.rs')
-rw-r--r-- | vendor/time/src/duration.rs | 1604 |
1 files changed, 1044 insertions, 560 deletions
diff --git a/vendor/time/src/duration.rs b/vendor/time/src/duration.rs index b33206c1e..f8d916f45 100644 --- a/vendor/time/src/duration.rs +++ b/vendor/time/src/duration.rs @@ -1,655 +1,1139 @@ -// 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 - -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, Hash)] -pub struct Duration { - secs: i64, - nanos: i32, // Always 0 <= nanos < NANOS_PER_SEC +//! The [`Duration`] struct and its associated `impl`s. + +use core::cmp::Ordering; +use core::fmt; +use core::iter::Sum; +use core::ops::{Add, AddAssign, Div, Mul, Neg, Sub, SubAssign}; +use core::time::Duration as StdDuration; + +use crate::error; +#[cfg(feature = "std")] +use crate::Instant; + +/// By explicitly inserting this enum where padding is expected, the compiler is able to better +/// perform niche value optimization. +#[repr(u32)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub(crate) enum Padding { + #[allow(clippy::missing_docs_in_private_items)] + Optimize, +} + +impl Default for Padding { + fn default() -> Self { + Self::Optimize + } } -/// 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 -}; +/// A span of time with nanosecond precision. +/// +/// Each `Duration` is composed of a whole number of seconds and a fractional part represented in +/// nanoseconds. +/// +/// This implementation allows for negative durations, unlike [`core::time::Duration`]. +#[derive(Clone, Copy, Default, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct Duration { + /// Number of whole seconds. + seconds: i64, + /// Number of nanoseconds within the second. The sign always matches the `seconds` field. + nanoseconds: i32, // always -10^9 < nanoseconds < 10^9 + #[allow(clippy::missing_docs_in_private_items)] + padding: Padding, +} -/// 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 fmt::Debug for Duration { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Duration") + .field("seconds", &self.seconds) + .field("nanoseconds", &self.nanoseconds) + .finish() + } +} 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 out 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 + // region: constants + /// Equivalent to `0.seconds()`. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(Duration::ZERO, 0.seconds()); + /// ``` + pub const ZERO: Self = Self::seconds(0); + + /// Equivalent to `1.nanoseconds()`. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(Duration::NANOSECOND, 1.nanoseconds()); + /// ``` + pub const NANOSECOND: Self = Self::nanoseconds(1); + + /// Equivalent to `1.microseconds()`. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(Duration::MICROSECOND, 1.microseconds()); + /// ``` + pub const MICROSECOND: Self = Self::microseconds(1); + + /// Equivalent to `1.milliseconds()`. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(Duration::MILLISECOND, 1.milliseconds()); + /// ``` + pub const MILLISECOND: Self = Self::milliseconds(1); + + /// Equivalent to `1.seconds()`. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(Duration::SECOND, 1.seconds()); + /// ``` + pub const SECOND: Self = Self::seconds(1); + + /// Equivalent to `1.minutes()`. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(Duration::MINUTE, 1.minutes()); + /// ``` + pub const MINUTE: Self = Self::minutes(1); + + /// Equivalent to `1.hours()`. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(Duration::HOUR, 1.hours()); + /// ``` + pub const HOUR: Self = Self::hours(1); + + /// Equivalent to `1.days()`. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(Duration::DAY, 1.days()); + /// ``` + pub const DAY: Self = Self::days(1); + + /// Equivalent to `1.weeks()`. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(Duration::WEEK, 1.weeks()); + /// ``` + pub const WEEK: Self = Self::weeks(1); + + /// The minimum possible duration. Adding any negative duration to this will cause an overflow. + pub const MIN: Self = Self::new_unchecked(i64::MIN, -999_999_999); + + /// The maximum possible duration. Adding any positive duration to this will cause an overflow. + pub const MAX: Self = Self::new_unchecked(i64::MAX, 999_999_999); + // endregion constants + + // region: is_{sign} + /// Check if a duration is exactly zero. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// assert!(0.seconds().is_zero()); + /// assert!(!1.nanoseconds().is_zero()); + /// ``` + pub const fn is_zero(self) -> bool { + self.seconds == 0 && self.nanoseconds == 0 } - /// 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 } + /// Check if a duration is negative. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// assert!((-1).seconds().is_negative()); + /// assert!(!0.seconds().is_negative()); + /// assert!(!1.seconds().is_negative()); + /// ``` + pub const fn is_negative(self) -> bool { + self.seconds < 0 || self.nanoseconds < 0 } - /// 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 } + /// Check if a duration is positive. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// assert!(1.seconds().is_positive()); + /// assert!(!0.seconds().is_positive()); + /// assert!(!(-1).seconds().is_positive()); + /// ``` + pub const fn is_positive(self) -> bool { + self.seconds > 0 || self.nanoseconds > 0 } + // endregion is_{sign} - /// 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 } + // region: abs + /// Get the absolute value of the duration. + /// + /// This method saturates the returned value if it would otherwise overflow. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// assert_eq!(1.seconds().abs(), 1.seconds()); + /// assert_eq!(0.seconds().abs(), 0.seconds()); + /// assert_eq!((-1).seconds().abs(), 1.seconds()); + /// ``` + pub const fn abs(self) -> Self { + Self::new_unchecked(self.seconds.saturating_abs(), self.nanoseconds.abs()) } - /// Runs a closure, returning the duration of time it took to run the - /// closure. - pub fn span<F>(f: F) -> Duration where F: FnOnce() { - let before = super::precise_time_ns(); - f(); - Duration::nanoseconds((super::precise_time_ns() - before) as i64) + /// Convert the existing `Duration` to a `std::time::Duration` and its sign. This returns a + /// [`std::time::Duration`] and does not saturate the returned value (unlike [`Duration::abs`]). + /// + /// ```rust + /// # use time::ext::{NumericalDuration, NumericalStdDuration}; + /// assert_eq!(1.seconds().unsigned_abs(), 1.std_seconds()); + /// assert_eq!(0.seconds().unsigned_abs(), 0.std_seconds()); + /// assert_eq!((-1).seconds().unsigned_abs(), 1.std_seconds()); + /// ``` + pub const fn unsigned_abs(self) -> StdDuration { + StdDuration::new(self.seconds.unsigned_abs(), self.nanoseconds.unsigned_abs()) } + // endregion abs + + // region: constructors + /// Create a new `Duration` without checking the validity of the components. + pub(crate) const fn new_unchecked(seconds: i64, nanoseconds: i32) -> Self { + if seconds < 0 { + debug_assert!(nanoseconds <= 0); + debug_assert!(nanoseconds > -1_000_000_000); + } else if seconds > 0 { + debug_assert!(nanoseconds >= 0); + debug_assert!(nanoseconds < 1_000_000_000); + } else { + debug_assert!(nanoseconds.unsigned_abs() < 1_000_000_000); + } - /// Returns the total number of whole weeks in the duration. - #[inline] - pub fn num_weeks(&self) -> i64 { - self.num_days() / 7 + Self { + seconds, + nanoseconds, + padding: Padding::Optimize, + } } - /// Returns the total number of whole days in the duration. - pub fn num_days(&self) -> i64 { - self.num_seconds() / SECS_PER_DAY + /// Create a new `Duration` with the provided seconds and nanoseconds. If nanoseconds is at + /// least ±10<sup>9</sup>, it will wrap to the number of seconds. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(Duration::new(1, 0), 1.seconds()); + /// assert_eq!(Duration::new(-1, 0), (-1).seconds()); + /// assert_eq!(Duration::new(1, 2_000_000_000), 3.seconds()); + /// ``` + pub const fn new(mut seconds: i64, mut nanoseconds: i32) -> Self { + seconds = expect_opt!( + seconds.checked_add(nanoseconds as i64 / 1_000_000_000), + "overflow constructing `time::Duration`" + ); + nanoseconds %= 1_000_000_000; + + if seconds > 0 && nanoseconds < 0 { + // `seconds` cannot overflow here because it is positive. + seconds -= 1; + nanoseconds += 1_000_000_000; + } else if seconds < 0 && nanoseconds > 0 { + // `seconds` cannot overflow here because it is negative. + seconds += 1; + nanoseconds -= 1_000_000_000; + } + + Self::new_unchecked(seconds, nanoseconds) } - /// Returns the total number of whole hours in the duration. - #[inline] - pub fn num_hours(&self) -> i64 { - self.num_seconds() / SECS_PER_HOUR + /// Create a new `Duration` with the given number of weeks. Equivalent to + /// `Duration::seconds(weeks * 604_800)`. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(Duration::weeks(1), 604_800.seconds()); + /// ``` + pub const fn weeks(weeks: i64) -> Self { + Self::seconds(expect_opt!( + weeks.checked_mul(604_800), + "overflow constructing `time::Duration`" + )) } - /// Returns the total number of whole minutes in the duration. - #[inline] - pub fn num_minutes(&self) -> i64 { - self.num_seconds() / SECS_PER_MINUTE + /// Create a new `Duration` with the given number of days. Equivalent to + /// `Duration::seconds(days * 86_400)`. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(Duration::days(1), 86_400.seconds()); + /// ``` + pub const fn days(days: i64) -> Self { + Self::seconds(expect_opt!( + days.checked_mul(86_400), + "overflow constructing `time::Duration`" + )) } - /// 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 + /// Create a new `Duration` with the given number of hours. Equivalent to + /// `Duration::seconds(hours * 3_600)`. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(Duration::hours(1), 3_600.seconds()); + /// ``` + pub const fn hours(hours: i64) -> Self { + Self::seconds(expect_opt!( + hours.checked_mul(3_600), + "overflow constructing `time::Duration`" + )) + } + + /// Create a new `Duration` with the given number of minutes. Equivalent to + /// `Duration::seconds(minutes * 60)`. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(Duration::minutes(1), 60.seconds()); + /// ``` + pub const fn minutes(minutes: i64) -> Self { + Self::seconds(expect_opt!( + minutes.checked_mul(60), + "overflow constructing `time::Duration`" + )) + } + + /// Create a new `Duration` with the given number of seconds. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(Duration::seconds(1), 1_000.milliseconds()); + /// ``` + pub const fn seconds(seconds: i64) -> Self { + Self::new_unchecked(seconds, 0) + } + + /// Creates a new `Duration` from the specified number of seconds represented as `f64`. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(Duration::seconds_f64(0.5), 0.5.seconds()); + /// assert_eq!(Duration::seconds_f64(-0.5), -0.5.seconds()); + /// ``` + pub fn seconds_f64(seconds: f64) -> Self { + if seconds > i64::MAX as f64 || seconds < i64::MIN as f64 { + crate::expect_failed("overflow constructing `time::Duration`"); + } + if seconds.is_nan() { + crate::expect_failed("passed NaN to `time::Duration::seconds_f64`"); } + Self::new_unchecked(seconds as _, ((seconds % 1.) * 1_000_000_000.) as _) } - /// 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 + /// Creates a new `Duration` from the specified number of seconds represented as `f32`. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(Duration::seconds_f32(0.5), 0.5.seconds()); + /// assert_eq!(Duration::seconds_f32(-0.5), (-0.5).seconds()); + /// ``` + pub fn seconds_f32(seconds: f32) -> Self { + if seconds > i64::MAX as f32 || seconds < i64::MIN as f32 { + crate::expect_failed("overflow constructing `time::Duration`"); } + if seconds.is_nan() { + crate::expect_failed("passed NaN to `time::Duration::seconds_f32`"); + } + Self::new_unchecked(seconds as _, ((seconds % 1.) * 1_000_000_000.) as _) } - /// 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 + /// Create a new `Duration` with the given number of milliseconds. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(Duration::milliseconds(1), 1_000.microseconds()); + /// assert_eq!(Duration::milliseconds(-1), (-1_000).microseconds()); + /// ``` + pub const fn milliseconds(milliseconds: i64) -> Self { + Self::new_unchecked( + milliseconds / 1_000, + ((milliseconds % 1_000) * 1_000_000) as _, + ) } - /// Returns the total number of whole microseconds in the duration, - /// or `None` on overflow (exceeding 2<sup>63</sup> microseconds in either direction). - pub fn num_microseconds(&self) -> Option<i64> { - 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) + /// Create a new `Duration` with the given number of microseconds. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(Duration::microseconds(1), 1_000.nanoseconds()); + /// assert_eq!(Duration::microseconds(-1), (-1_000).nanoseconds()); + /// ``` + pub const fn microseconds(microseconds: i64) -> Self { + Self::new_unchecked( + microseconds / 1_000_000, + ((microseconds % 1_000_000) * 1_000) as _, + ) } - /// Returns the total number of whole nanoseconds in the duration, - /// or `None` on overflow (exceeding 2<sup>63</sup> nanoseconds in either direction). - pub fn num_nanoseconds(&self) -> Option<i64> { - 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) + /// Create a new `Duration` with the given number of nanoseconds. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(Duration::nanoseconds(1), 1.microseconds() / 1_000); + /// assert_eq!(Duration::nanoseconds(-1), (-1).microseconds() / 1_000); + /// ``` + pub const fn nanoseconds(nanoseconds: i64) -> Self { + Self::new_unchecked( + nanoseconds / 1_000_000_000, + (nanoseconds % 1_000_000_000) as _, + ) } - /// Add two durations, returning `None` if overflow occurred. - pub fn checked_add(&self, rhs: &Duration) -> Option<Duration> { - 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)); + /// Create a new `Duration` with the given number of nanoseconds. + /// + /// As the input range cannot be fully mapped to the output, this should only be used where it's + /// known to result in a valid value. + pub(crate) const fn nanoseconds_i128(nanoseconds: i128) -> Self { + let seconds = nanoseconds / 1_000_000_000; + let nanoseconds = nanoseconds % 1_000_000_000; + + if seconds > i64::MAX as i128 || seconds < i64::MIN as i128 { + crate::expect_failed("overflow constructing `time::Duration`"); } - 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<Duration> { - 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)); + + Self::new_unchecked(seconds as _, nanoseconds as _) + } + // endregion constructors + + // region: getters + /// Get the number of whole weeks in the duration. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// assert_eq!(1.weeks().whole_weeks(), 1); + /// assert_eq!((-1).weeks().whole_weeks(), -1); + /// assert_eq!(6.days().whole_weeks(), 0); + /// assert_eq!((-6).days().whole_weeks(), 0); + /// ``` + pub const fn whole_weeks(self) -> i64 { + self.whole_seconds() / 604_800 + } + + /// Get the number of whole days in the duration. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// assert_eq!(1.days().whole_days(), 1); + /// assert_eq!((-1).days().whole_days(), -1); + /// assert_eq!(23.hours().whole_days(), 0); + /// assert_eq!((-23).hours().whole_days(), 0); + /// ``` + pub const fn whole_days(self) -> i64 { + self.whole_seconds() / 86_400 + } + + /// Get the number of whole hours in the duration. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// assert_eq!(1.hours().whole_hours(), 1); + /// assert_eq!((-1).hours().whole_hours(), -1); + /// assert_eq!(59.minutes().whole_hours(), 0); + /// assert_eq!((-59).minutes().whole_hours(), 0); + /// ``` + pub const fn whole_hours(self) -> i64 { + self.whole_seconds() / 3_600 + } + + /// Get the number of whole minutes in the duration. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// assert_eq!(1.minutes().whole_minutes(), 1); + /// assert_eq!((-1).minutes().whole_minutes(), -1); + /// assert_eq!(59.seconds().whole_minutes(), 0); + /// assert_eq!((-59).seconds().whole_minutes(), 0); + /// ``` + pub const fn whole_minutes(self) -> i64 { + self.whole_seconds() / 60 + } + + /// Get the number of whole seconds in the duration. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// assert_eq!(1.seconds().whole_seconds(), 1); + /// assert_eq!((-1).seconds().whole_seconds(), -1); + /// assert_eq!(1.minutes().whole_seconds(), 60); + /// assert_eq!((-1).minutes().whole_seconds(), -60); + /// ``` + pub const fn whole_seconds(self) -> i64 { + self.seconds + } + + /// Get the number of fractional seconds in the duration. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// assert_eq!(1.5.seconds().as_seconds_f64(), 1.5); + /// assert_eq!((-1.5).seconds().as_seconds_f64(), -1.5); + /// ``` + pub fn as_seconds_f64(self) -> f64 { + self.seconds as f64 + self.nanoseconds as f64 / 1_000_000_000. + } + + /// Get the number of fractional seconds in the duration. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// assert_eq!(1.5.seconds().as_seconds_f32(), 1.5); + /// assert_eq!((-1.5).seconds().as_seconds_f32(), -1.5); + /// ``` + pub fn as_seconds_f32(self) -> f32 { + self.seconds as f32 + self.nanoseconds as f32 / 1_000_000_000. + } + + /// Get the number of whole milliseconds in the duration. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// assert_eq!(1.seconds().whole_milliseconds(), 1_000); + /// assert_eq!((-1).seconds().whole_milliseconds(), -1_000); + /// assert_eq!(1.milliseconds().whole_milliseconds(), 1); + /// assert_eq!((-1).milliseconds().whole_milliseconds(), -1); + /// ``` + pub const fn whole_milliseconds(self) -> i128 { + self.seconds as i128 * 1_000 + self.nanoseconds as i128 / 1_000_000 + } + + /// Get the number of milliseconds past the number of whole seconds. + /// + /// Always in the range `-1_000..1_000`. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// assert_eq!(1.4.seconds().subsec_milliseconds(), 400); + /// assert_eq!((-1.4).seconds().subsec_milliseconds(), -400); + /// ``` + // Allow the lint, as the value is guaranteed to be less than 1000. + pub const fn subsec_milliseconds(self) -> i16 { + (self.nanoseconds / 1_000_000) as _ + } + + /// Get the number of whole microseconds in the duration. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// assert_eq!(1.milliseconds().whole_microseconds(), 1_000); + /// assert_eq!((-1).milliseconds().whole_microseconds(), -1_000); + /// assert_eq!(1.microseconds().whole_microseconds(), 1); + /// assert_eq!((-1).microseconds().whole_microseconds(), -1); + /// ``` + pub const fn whole_microseconds(self) -> i128 { + self.seconds as i128 * 1_000_000 + self.nanoseconds as i128 / 1_000 + } + + /// Get the number of microseconds past the number of whole seconds. + /// + /// Always in the range `-1_000_000..1_000_000`. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// assert_eq!(1.0004.seconds().subsec_microseconds(), 400); + /// assert_eq!((-1.0004).seconds().subsec_microseconds(), -400); + /// ``` + pub const fn subsec_microseconds(self) -> i32 { + self.nanoseconds / 1_000 + } + + /// Get the number of nanoseconds in the duration. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// assert_eq!(1.microseconds().whole_nanoseconds(), 1_000); + /// assert_eq!((-1).microseconds().whole_nanoseconds(), -1_000); + /// assert_eq!(1.nanoseconds().whole_nanoseconds(), 1); + /// assert_eq!((-1).nanoseconds().whole_nanoseconds(), -1); + /// ``` + pub const fn whole_nanoseconds(self) -> i128 { + self.seconds as i128 * 1_000_000_000 + self.nanoseconds as i128 + } + + /// Get the number of nanoseconds past the number of whole seconds. + /// + /// The returned value will always be in the range `-1_000_000_000..1_000_000_000`. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// assert_eq!(1.000_000_400.seconds().subsec_nanoseconds(), 400); + /// assert_eq!((-1.000_000_400).seconds().subsec_nanoseconds(), -400); + /// ``` + pub const fn subsec_nanoseconds(self) -> i32 { + self.nanoseconds + } + // endregion getters + + // region: checked arithmetic + /// Computes `self + rhs`, returning `None` if an overflow occurred. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(5.seconds().checked_add(5.seconds()), Some(10.seconds())); + /// assert_eq!(Duration::MAX.checked_add(1.nanoseconds()), None); + /// assert_eq!((-5).seconds().checked_add(5.seconds()), Some(0.seconds())); + /// ``` + pub const fn checked_add(self, rhs: Self) -> Option<Self> { + let mut seconds = const_try_opt!(self.seconds.checked_add(rhs.seconds)); + let mut nanoseconds = self.nanoseconds + rhs.nanoseconds; + + if nanoseconds >= 1_000_000_000 || seconds < 0 && nanoseconds > 0 { + nanoseconds -= 1_000_000_000; + seconds = const_try_opt!(seconds.checked_add(1)); + } else if nanoseconds <= -1_000_000_000 || seconds > 0 && nanoseconds < 0 { + nanoseconds += 1_000_000_000; + seconds = const_try_opt!(seconds.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) } + + Some(Self::new_unchecked(seconds, nanoseconds)) } - /// The minimum possible `Duration`: `i64::MIN` milliseconds. - #[inline] - pub fn min_value() -> Duration { MIN } + /// Computes `self - rhs`, returning `None` if an overflow occurred. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(5.seconds().checked_sub(5.seconds()), Some(Duration::ZERO)); + /// assert_eq!(Duration::MIN.checked_sub(1.nanoseconds()), None); + /// assert_eq!(5.seconds().checked_sub(10.seconds()), Some((-5).seconds())); + /// ``` + pub const fn checked_sub(self, rhs: Self) -> Option<Self> { + let mut seconds = const_try_opt!(self.seconds.checked_sub(rhs.seconds)); + let mut nanoseconds = self.nanoseconds - rhs.nanoseconds; + + if nanoseconds >= 1_000_000_000 || seconds < 0 && nanoseconds > 0 { + nanoseconds -= 1_000_000_000; + seconds = const_try_opt!(seconds.checked_add(1)); + } else if nanoseconds <= -1_000_000_000 || seconds > 0 && nanoseconds < 0 { + nanoseconds += 1_000_000_000; + seconds = const_try_opt!(seconds.checked_sub(1)); + } - /// The maximum possible `Duration`: `i64::MAX` milliseconds. - #[inline] - pub fn max_value() -> Duration { MAX } + Some(Self::new_unchecked(seconds, nanoseconds)) + } - /// A duration where the stored seconds and nanoseconds are equal to zero. - #[inline] - pub fn zero() -> Duration { - Duration { secs: 0, nanos: 0 } + /// Computes `self * rhs`, returning `None` if an overflow occurred. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(5.seconds().checked_mul(2), Some(10.seconds())); + /// assert_eq!(5.seconds().checked_mul(-2), Some((-10).seconds())); + /// assert_eq!(5.seconds().checked_mul(0), Some(0.seconds())); + /// assert_eq!(Duration::MAX.checked_mul(2), None); + /// assert_eq!(Duration::MIN.checked_mul(2), None); + /// ``` + pub const fn checked_mul(self, rhs: i32) -> Option<Self> { + // Multiply nanoseconds as i64, because it cannot overflow that way. + let total_nanos = self.nanoseconds as i64 * rhs as i64; + let extra_secs = total_nanos / 1_000_000_000; + let nanoseconds = (total_nanos % 1_000_000_000) as _; + let seconds = const_try_opt!( + const_try_opt!(self.seconds.checked_mul(rhs as _)).checked_add(extra_secs) + ); + + Some(Self::new_unchecked(seconds, nanoseconds)) } - /// Returns `true` if the duration equals `Duration::zero()`. - #[inline] - pub fn is_zero(&self) -> bool { - self.secs == 0 && self.nanos == 0 + /// Computes `self / rhs`, returning `None` if `rhs == 0` or if the result would overflow. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// assert_eq!(10.seconds().checked_div(2), Some(5.seconds())); + /// assert_eq!(10.seconds().checked_div(-2), Some((-5).seconds())); + /// assert_eq!(1.seconds().checked_div(0), None); + /// ``` + pub const fn checked_div(self, rhs: i32) -> Option<Self> { + let seconds = const_try_opt!(self.seconds.checked_div(rhs as i64)); + let carry = self.seconds - seconds * (rhs as i64); + let extra_nanos = const_try_opt!((carry * 1_000_000_000).checked_div(rhs as i64)); + let nanoseconds = const_try_opt!(self.nanoseconds.checked_div(rhs)) + (extra_nanos as i32); + + Some(Self::new_unchecked(seconds, nanoseconds)) + } + // endregion checked arithmetic + + // region: saturating arithmetic + /// Computes `self + rhs`, saturating if an overflow occurred. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(5.seconds().saturating_add(5.seconds()), 10.seconds()); + /// assert_eq!(Duration::MAX.saturating_add(1.nanoseconds()), Duration::MAX); + /// assert_eq!( + /// Duration::MIN.saturating_add((-1).nanoseconds()), + /// Duration::MIN + /// ); + /// assert_eq!((-5).seconds().saturating_add(5.seconds()), Duration::ZERO); + /// ``` + pub const fn saturating_add(self, rhs: Self) -> Self { + let (mut seconds, overflow) = self.seconds.overflowing_add(rhs.seconds); + if overflow { + if self.seconds > 0 { + return Self::MAX; + } + return Self::MIN; + } + let mut nanoseconds = self.nanoseconds + rhs.nanoseconds; + + if nanoseconds >= 1_000_000_000 || seconds < 0 && nanoseconds > 0 { + nanoseconds -= 1_000_000_000; + seconds = match seconds.checked_add(1) { + Some(seconds) => seconds, + None => return Self::MAX, + }; + } else if nanoseconds <= -1_000_000_000 || seconds > 0 && nanoseconds < 0 { + nanoseconds += 1_000_000_000; + seconds = match seconds.checked_sub(1) { + Some(seconds) => seconds, + None => return Self::MIN, + }; + } + + Self::new_unchecked(seconds, nanoseconds) } - /// Creates a `time::Duration` object from `std::time::Duration` + /// Computes `self - rhs`, saturating if an overflow occurred. /// - /// This function errors when original duration is larger than the maximum - /// value supported for this type. - pub fn from_std(duration: StdDuration) -> Result<Duration, OutOfRangeError> { - // We need to check secs as u64 before coercing to i64 - if duration.as_secs() > MAX.secs as u64 { - return Err(OutOfRangeError(())); + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(5.seconds().saturating_sub(5.seconds()), Duration::ZERO); + /// assert_eq!(Duration::MIN.saturating_sub(1.nanoseconds()), Duration::MIN); + /// assert_eq!( + /// Duration::MAX.saturating_sub((-1).nanoseconds()), + /// Duration::MAX + /// ); + /// assert_eq!(5.seconds().saturating_sub(10.seconds()), (-5).seconds()); + /// ``` + pub const fn saturating_sub(self, rhs: Self) -> Self { + let (mut seconds, overflow) = self.seconds.overflowing_sub(rhs.seconds); + if overflow { + if self.seconds > 0 { + return Self::MAX; + } + return Self::MIN; } - let d = Duration { - secs: duration.as_secs() as i64, - nanos: duration.subsec_nanos() as i32, - }; - if d > MAX { - return Err(OutOfRangeError(())); + let mut nanoseconds = self.nanoseconds - rhs.nanoseconds; + + if nanoseconds >= 1_000_000_000 || seconds < 0 && nanoseconds > 0 { + nanoseconds -= 1_000_000_000; + seconds = match seconds.checked_add(1) { + Some(seconds) => seconds, + None => return Self::MAX, + }; + } else if nanoseconds <= -1_000_000_000 || seconds > 0 && nanoseconds < 0 { + nanoseconds += 1_000_000_000; + seconds = match seconds.checked_sub(1) { + Some(seconds) => seconds, + None => return Self::MIN, + }; } - Ok(d) + + Self::new_unchecked(seconds, nanoseconds) } - /// Creates a `std::time::Duration` object from `time::Duration` + /// Computes `self * rhs`, saturating if an overflow occurred. /// - /// 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<StdDuration, OutOfRangeError> { - if self.secs < 0 { - return Err(OutOfRangeError(())); + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(5.seconds().saturating_mul(2), 10.seconds()); + /// assert_eq!(5.seconds().saturating_mul(-2), (-10).seconds()); + /// assert_eq!(5.seconds().saturating_mul(0), Duration::ZERO); + /// assert_eq!(Duration::MAX.saturating_mul(2), Duration::MAX); + /// assert_eq!(Duration::MIN.saturating_mul(2), Duration::MIN); + /// assert_eq!(Duration::MAX.saturating_mul(-2), Duration::MIN); + /// assert_eq!(Duration::MIN.saturating_mul(-2), Duration::MAX); + /// ``` + pub const fn saturating_mul(self, rhs: i32) -> Self { + // Multiply nanoseconds as i64, because it cannot overflow that way. + let total_nanos = self.nanoseconds as i64 * rhs as i64; + let extra_secs = total_nanos / 1_000_000_000; + let nanoseconds = (total_nanos % 1_000_000_000) as _; + let (seconds, overflow1) = self.seconds.overflowing_mul(rhs as _); + if overflow1 { + if self.seconds > 0 && rhs > 0 || self.seconds < 0 && rhs < 0 { + return Self::MAX; + } + return Self::MIN; } - Ok(StdDuration::new(self.secs as u64, self.nanos as u32)) + let (seconds, overflow2) = seconds.overflowing_add(extra_secs); + if overflow2 { + if self.seconds > 0 && rhs > 0 { + return Self::MAX; + } + return Self::MIN; + } + + Self::new_unchecked(seconds, nanoseconds) } + // endregion saturating arithmetic + + /// Runs a closure, returning the duration of time it took to run. The return value of the + /// closure is provided in the second part of the tuple. + #[cfg(feature = "std")] + pub fn time_fn<T>(f: impl FnOnce() -> T) -> (Self, T) { + let start = Instant::now(); + let return_value = f(); + let end = Instant::now(); - /// Returns the raw value of duration. - #[cfg(target_env = "sgx")] - pub(crate) fn raw(&self) -> (i64, i32) { - (self.secs, self.nanos) + (end - start, return_value) } } -impl Neg for Duration { - type Output = Duration; +// region: trait impls +/// The format returned by this implementation is not stable and must not be relied upon. +/// +/// By default this produces an exact, full-precision printout of the duration. +/// For a concise, rounded printout instead, you can use the `.N` format specifier: +/// +/// ``` +/// # use time::Duration; +/// # +/// let duration = Duration::new(123456, 789011223); +/// println!("{duration:.3}"); +/// ``` +/// +/// For the purposes of this implementation, a day is exactly 24 hours and a minute is exactly 60 +/// seconds. +impl fmt::Display for Duration { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.is_negative() { + f.write_str("-")?; + } + + if let Some(_precision) = f.precision() { + // Concise, rounded representation. - #[inline] - fn neg(self) -> Duration { - if self.nanos == 0 { - Duration { secs: -self.secs, nanos: 0 } + if self.is_zero() { + // Write a zero value with the requested precision. + return (0.).fmt(f).and_then(|_| f.write_str("s")); + } + + /// Format the first item that produces a value greater than 1 and then break. + macro_rules! item { + ($name:literal, $value:expr) => { + let value = $value; + if value >= 1.0 { + return value.fmt(f).and_then(|_| f.write_str($name)); + } + }; + } + + // Even if this produces a de-normal float, because we're rounding we don't really care. + let seconds = self.unsigned_abs().as_secs_f64(); + + item!("d", seconds / 86_400.); + item!("h", seconds / 3_600.); + item!("m", seconds / 60.); + item!("s", seconds); + item!("ms", seconds * 1_000.); + item!("µs", seconds * 1_000_000.); + item!("ns", seconds * 1_000_000_000.); } else { - Duration { secs: -self.secs - 1, nanos: NANOS_PER_SEC - self.nanos } + // Precise, but verbose representation. + + if self.is_zero() { + return f.write_str("0s"); + } + + /// Format a single item. + macro_rules! item { + ($name:literal, $value:expr) => { + match $value { + 0 => Ok(()), + value => value.fmt(f).and_then(|_| f.write_str($name)), + } + }; + } + + let seconds = self.seconds.unsigned_abs(); + let nanoseconds = self.nanoseconds.unsigned_abs(); + + item!("d", seconds / 86_400)?; + item!("h", seconds / 3_600 % 24)?; + item!("m", seconds / 60 % 60)?; + item!("s", seconds % 60)?; + item!("ms", nanoseconds / 1_000_000)?; + item!("µs", nanoseconds / 1_000 % 1_000)?; + item!("ns", nanoseconds % 1_000)?; } + + Ok(()) + } +} + +impl TryFrom<StdDuration> for Duration { + type Error = error::ConversionRange; + + fn try_from(original: StdDuration) -> Result<Self, error::ConversionRange> { + Ok(Self::new( + original + .as_secs() + .try_into() + .map_err(|_| error::ConversionRange)?, + original.subsec_nanos() as _, + )) + } +} + +impl TryFrom<Duration> for StdDuration { + type Error = error::ConversionRange; + + fn try_from(duration: Duration) -> Result<Self, error::ConversionRange> { + Ok(Self::new( + duration + .seconds + .try_into() + .map_err(|_| error::ConversionRange)?, + duration + .nanoseconds + .try_into() + .map_err(|_| error::ConversionRange)?, + )) } } impl Add for Duration { + type Output = Self; + + fn add(self, rhs: Self) -> Self::Output { + self.checked_add(rhs) + .expect("overflow when adding durations") + } +} + +impl Add<StdDuration> for Duration { + type Output = Self; + + fn add(self, std_duration: StdDuration) -> Self::Output { + self + Self::try_from(std_duration) + .expect("overflow converting `std::time::Duration` to `time::Duration`") + } +} + +impl Add<Duration> for StdDuration { 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 } + fn add(self, rhs: Duration) -> Self::Output { + rhs + self + } +} + +impl_add_assign!(Duration: Self, StdDuration); + +impl AddAssign<Duration> for StdDuration { + fn add_assign(&mut self, rhs: Duration) { + *self = (*self + rhs).try_into().expect( + "Cannot represent a resulting duration in std. Try `let x = x + rhs;`, which will \ + change the type.", + ); + } +} + +impl Neg for Duration { + type Output = Self; + + fn neg(self) -> Self::Output { + Self::new_unchecked(-self.seconds, -self.nanoseconds) } } impl Sub for Duration { + type Output = Self; + + fn sub(self, rhs: Self) -> Self::Output { + self.checked_sub(rhs) + .expect("overflow when subtracting durations") + } +} + +impl Sub<StdDuration> for Duration { + type Output = Self; + + fn sub(self, rhs: StdDuration) -> Self::Output { + self - Self::try_from(rhs) + .expect("overflow converting `std::time::Duration` to `time::Duration`") + } +} + +impl Sub<Duration> for StdDuration { 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; + fn sub(self, rhs: Duration) -> Self::Output { + Duration::try_from(self) + .expect("overflow converting `std::time::Duration` to `time::Duration`") + - rhs + } +} + +impl_sub_assign!(Duration: Self, StdDuration); + +impl SubAssign<Duration> for StdDuration { + fn sub_assign(&mut self, rhs: Duration) { + *self = (*self - rhs).try_into().expect( + "Cannot represent a resulting duration in std. Try `let x = x - rhs;`, which will \ + change the type.", + ); + } +} + +/// Implement `Mul` (reflexively) and `Div` for `Duration` for various types. +macro_rules! duration_mul_div_int { + ($($type:ty),+) => {$( + impl Mul<$type> for Duration { + type Output = Self; + + fn mul(self, rhs: $type) -> Self::Output { + Self::nanoseconds_i128( + self.whole_nanoseconds() + .checked_mul(rhs as _) + .expect("overflow when multiplying duration") + ) + } + } + + impl Mul<Duration> for $type { + type Output = Duration; + + fn mul(self, rhs: Duration) -> Self::Output { + rhs * self + } + } + + impl Div<$type> for Duration { + type Output = Self; + + fn div(self, rhs: $type) -> Self::Output { + Self::nanoseconds_i128(self.whole_nanoseconds() / rhs as i128) + } } - Duration { secs: secs, nanos: nanos } + )+}; +} +duration_mul_div_int![i8, i16, i32, u8, u16, u32]; + +impl Mul<f32> for Duration { + type Output = Self; + + fn mul(self, rhs: f32) -> Self::Output { + Self::seconds_f32(self.as_seconds_f32() * rhs) } } -impl Mul<i32> for Duration { +impl Mul<Duration> for f32 { 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 } + fn mul(self, rhs: Duration) -> Self::Output { + rhs * self + } +} + +impl Mul<f64> for Duration { + type Output = Self; + + fn mul(self, rhs: f64) -> Self::Output { + Self::seconds_f64(self.as_seconds_f64() * rhs) } } -impl Div<i32> for Duration { +impl Mul<Duration> for f64 { 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 } + fn mul(self, rhs: Duration) -> Self::Output { + rhs * self } } -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, "") }; +impl_mul_assign!(Duration: i8, i16, i32, u8, u16, u32, f32, f64); - 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; +impl Div<f32> for Duration { + type Output = Self; - write!(f, "{}P", sign)?; + fn div(self, rhs: f32) -> Self::Output { + Self::seconds_f32(self.as_seconds_f32() / rhs) + } +} - if hasdate { - write!(f, "{}D", days)?; - } - if hastime { - if abs.nanos == 0 { - write!(f, "T{}S", secs)?; - } else if abs.nanos % NANOS_PER_MILLI == 0 { - write!(f, "T{}.{:03}S", secs, abs.nanos / NANOS_PER_MILLI)?; - } else if abs.nanos % NANOS_PER_MICRO == 0 { - write!(f, "T{}.{:06}S", secs, abs.nanos / NANOS_PER_MICRO)?; - } else { - write!(f, "T{}.{:09}S", secs, abs.nanos)?; - } +impl Div<f64> for Duration { + type Output = Self; + + fn div(self, rhs: f64) -> Self::Output { + Self::seconds_f64(self.as_seconds_f64() / rhs) + } +} + +impl_div_assign!(Duration: i8, i16, i32, u8, u16, u32, f32, f64); + +impl Div for Duration { + type Output = f64; + + fn div(self, rhs: Self) -> Self::Output { + self.as_seconds_f64() / rhs.as_seconds_f64() + } +} + +impl Div<StdDuration> for Duration { + type Output = f64; + + fn div(self, rhs: StdDuration) -> Self::Output { + self.as_seconds_f64() / rhs.as_secs_f64() + } +} + +impl Div<Duration> for StdDuration { + type Output = f64; + + fn div(self, rhs: Duration) -> Self::Output { + self.as_secs_f64() / rhs.as_seconds_f64() + } +} + +impl PartialEq<StdDuration> for Duration { + fn eq(&self, rhs: &StdDuration) -> bool { + Ok(*self) == Self::try_from(*rhs) + } +} + +impl PartialEq<Duration> for StdDuration { + fn eq(&self, rhs: &Duration) -> bool { + rhs == self + } +} + +impl PartialOrd<StdDuration> for Duration { + fn partial_cmp(&self, rhs: &StdDuration) -> Option<Ordering> { + if rhs.as_secs() > i64::MAX as _ { + return Some(Ordering::Less); } - Ok(()) + + Some( + self.seconds + .cmp(&(rhs.as_secs() as _)) + .then_with(|| self.nanoseconds.cmp(&(rhs.subsec_nanos() as _))), + ) } } -/// 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 { - #[allow(deprecated)] - 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(()))); +impl PartialOrd<Duration> for StdDuration { + fn partial_cmp(&self, rhs: &Duration) -> Option<Ordering> { + rhs.partial_cmp(self).map(Ordering::reverse) + } +} + +impl Sum for Duration { + fn sum<I: Iterator<Item = Self>>(iter: I) -> Self { + iter.reduce(|a, b| a + b).unwrap_or_default() + } +} + +impl<'a> Sum<&'a Self> for Duration { + fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self { + iter.copied().sum() } } +// endregion trait impls |