summaryrefslogtreecommitdiffstats
path: root/vendor/time/src/duration.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/time/src/duration.rs')
-rw-r--r--vendor/time/src/duration.rs1604
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