| //! 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 deranged::RangedI32; |
| use num_conv::prelude::*; |
| |
| use crate::convert::*; |
| use crate::error; |
| use crate::internal_macros::{ |
| const_try_opt, expect_opt, impl_add_assign, impl_div_assign, impl_mul_assign, impl_sub_assign, |
| }; |
| #[cfg(feature = "std")] |
| #[allow(deprecated)] |
| 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, |
| } |
| |
| /// The type of the `nanosecond` field of `Duration`. |
| type Nanoseconds = |
| RangedI32<{ -(Nanosecond::per(Second) as i32 - 1) }, { Nanosecond::per(Second) as i32 - 1 }>; |
| |
| /// 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, 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. |
| // Sign must match that of `seconds` (though this is not a safety requirement). |
| nanoseconds: Nanoseconds, |
| #[allow(clippy::missing_docs_in_private_items)] |
| padding: Padding, |
| } |
| |
| 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 Default for Duration { |
| fn default() -> Self { |
| Self { |
| seconds: 0, |
| nanoseconds: Nanoseconds::new_static::<0>(), |
| padding: Padding::Optimize, |
| } |
| } |
| } |
| |
| /// This is adapted from the [`std` implementation][std], which uses mostly bit |
| /// operations to ensure the highest precision: |
| /// |
| /// Changes from `std` are marked and explained below. |
| /// |
| /// [std]: https://github.com/rust-lang/rust/blob/3a37c2f0523c87147b64f1b8099fc9df22e8c53e/library/core/src/time.rs#L1262-L1340 |
| #[rustfmt::skip] // Skip `rustfmt` because it reformats the arguments of the macro weirdly. |
| macro_rules! try_from_secs { |
| ( |
| secs = $secs: expr, |
| mantissa_bits = $mant_bits: literal, |
| exponent_bits = $exp_bits: literal, |
| offset = $offset: literal, |
| bits_ty = $bits_ty:ty, |
| bits_ty_signed = $bits_ty_signed:ty, |
| double_ty = $double_ty:ty, |
| float_ty = $float_ty:ty, |
| is_nan = $is_nan:expr, |
| is_overflow = $is_overflow:expr, |
| ) => {{ |
| 'value: { |
| const MIN_EXP: i16 = 1 - (1i16 << $exp_bits) / 2; |
| const MANT_MASK: $bits_ty = (1 << $mant_bits) - 1; |
| const EXP_MASK: $bits_ty = (1 << $exp_bits) - 1; |
| |
| // Change from std: No error check for negative values necessary. |
| |
| let bits = $secs.to_bits(); |
| let mant = (bits & MANT_MASK) | (MANT_MASK + 1); |
| let exp = ((bits >> $mant_bits) & EXP_MASK) as i16 + MIN_EXP; |
| |
| let (secs, nanos) = if exp < -31 { |
| // the input represents less than 1ns and can not be rounded to it |
| (0u64, 0u32) |
| } else if exp < 0 { |
| // the input is less than 1 second |
| let t = <$double_ty>::from(mant) << ($offset + exp); |
| let nanos_offset = $mant_bits + $offset; |
| let nanos_tmp = u128::from(Nanosecond::per(Second)) * u128::from(t); |
| let nanos = (nanos_tmp >> nanos_offset) as u32; |
| |
| let rem_mask = (1 << nanos_offset) - 1; |
| let rem_msb_mask = 1 << (nanos_offset - 1); |
| let rem = nanos_tmp & rem_mask; |
| let is_tie = rem == rem_msb_mask; |
| let is_even = (nanos & 1) == 0; |
| let rem_msb = nanos_tmp & rem_msb_mask == 0; |
| let add_ns = !(rem_msb || (is_even && is_tie)); |
| |
| // f32 does not have enough precision to trigger the second branch |
| // since it can not represent numbers between 0.999_999_940_395 and 1.0. |
| let nanos = nanos + add_ns as u32; |
| if ($mant_bits == 23) || (nanos != Nanosecond::per(Second)) { |
| (0, nanos) |
| } else { |
| (1, 0) |
| } |
| } else if exp < $mant_bits { |
| let secs = u64::from(mant >> ($mant_bits - exp)); |
| let t = <$double_ty>::from((mant << exp) & MANT_MASK); |
| let nanos_offset = $mant_bits; |
| let nanos_tmp = <$double_ty>::from(Nanosecond::per(Second)) * t; |
| let nanos = (nanos_tmp >> nanos_offset) as u32; |
| |
| let rem_mask = (1 << nanos_offset) - 1; |
| let rem_msb_mask = 1 << (nanos_offset - 1); |
| let rem = nanos_tmp & rem_mask; |
| let is_tie = rem == rem_msb_mask; |
| let is_even = (nanos & 1) == 0; |
| let rem_msb = nanos_tmp & rem_msb_mask == 0; |
| let add_ns = !(rem_msb || (is_even && is_tie)); |
| |
| // f32 does not have enough precision to trigger the second branch. |
| // For example, it can not represent numbers between 1.999_999_880... |
| // and 2.0. Bigger values result in even smaller precision of the |
| // fractional part. |
| let nanos = nanos + add_ns as u32; |
| if ($mant_bits == 23) || (nanos != Nanosecond::per(Second)) { |
| (secs, nanos) |
| } else { |
| (secs + 1, 0) |
| } |
| } else if exp < 63 { |
| // Change from std: The exponent here is 63 instead of 64, |
| // because i64::MAX + 1 is 2^63. |
| |
| // the input has no fractional part |
| let secs = u64::from(mant) << (exp - $mant_bits); |
| (secs, 0) |
| } else if bits == (i64::MIN as $float_ty).to_bits() { |
| // Change from std: Signed integers are asymmetrical in that |
| // iN::MIN is -iN::MAX - 1. So for example i8 covers the |
| // following numbers -128..=127. The check above (exp < 63) |
| // doesn't cover i64::MIN as that is -2^63, so we have this |
| // additional case to handle the asymmetry of iN::MIN. |
| break 'value Self::new_ranged_unchecked(i64::MIN, Nanoseconds::new_static::<0>()); |
| } else if $secs.is_nan() { |
| // Change from std: std doesn't differentiate between the error |
| // cases. |
| $is_nan |
| } else { |
| $is_overflow |
| }; |
| |
| // Change from std: All the code is mostly unmodified in that it |
| // simply calculates an unsigned integer. Here we extract the sign |
| // bit and assign it to the number. We basically manually do two's |
| // complement here, we could also use an if and just negate the |
| // numbers based on the sign, but it turns out to be quite a bit |
| // slower. |
| let mask = (bits as $bits_ty_signed) >> ($mant_bits + $exp_bits); |
| #[allow(trivial_numeric_casts)] |
| let secs_signed = ((secs as i64) ^ (mask as i64)) - (mask as i64); |
| #[allow(trivial_numeric_casts)] |
| let nanos_signed = ((nanos as i32) ^ (mask as i32)) - (mask as i32); |
| // Safety: `nanos_signed` is in range. |
| unsafe { Self::new_unchecked(secs_signed, nanos_signed) } |
| } |
| }}; |
| } |
| |
| impl Duration { |
| // 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_ranged(i64::MIN, Nanoseconds::MIN); |
| |
| /// The maximum possible duration. Adding any positive duration to this will cause an overflow. |
| pub const MAX: Self = Self::new_ranged(i64::MAX, Nanoseconds::MAX); |
| // 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.get() == 0 |
| } |
| |
| /// 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.get() < 0 |
| } |
| |
| /// 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.get() > 0 |
| } |
| // endregion is_{sign} |
| |
| // 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 { |
| match self.seconds.checked_abs() { |
| Some(seconds) => Self::new_ranged_unchecked(seconds, self.nanoseconds.abs()), |
| None => Self::MAX, |
| } |
| } |
| |
| /// 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.get().unsigned_abs(), |
| ) |
| } |
| // endregion abs |
| |
| // region: constructors |
| /// Create a new `Duration` without checking the validity of the components. |
| /// |
| /// # Safety |
| /// |
| /// - `nanoseconds` must be in the range `-999_999_999..=999_999_999`. |
| /// |
| /// While the sign of `nanoseconds` is required to be the same as the sign of `seconds`, this is |
| /// not a safety invariant. |
| pub(crate) const unsafe fn new_unchecked(seconds: i64, nanoseconds: i32) -> Self { |
| Self::new_ranged_unchecked( |
| seconds, |
| // Safety: The caller must uphold the safety invariants. |
| unsafe { Nanoseconds::new_unchecked(nanoseconds) }, |
| ) |
| } |
| |
| /// Create a new `Duration` without checking the validity of the components. |
| pub(crate) const fn new_ranged_unchecked(seconds: i64, nanoseconds: Nanoseconds) -> Self { |
| if seconds < 0 { |
| debug_assert!(nanoseconds.get() <= 0); |
| } else if seconds > 0 { |
| debug_assert!(nanoseconds.get() >= 0); |
| } |
| |
| Self { |
| seconds, |
| nanoseconds, |
| padding: Padding::Optimize, |
| } |
| } |
| |
| /// 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()); |
| /// ``` |
| /// |
| /// # Panics |
| /// |
| /// This may panic if an overflow occurs. |
| pub const fn new(mut seconds: i64, mut nanoseconds: i32) -> Self { |
| seconds = expect_opt!( |
| seconds.checked_add(nanoseconds as i64 / Nanosecond::per(Second) as i64), |
| "overflow constructing `time::Duration`" |
| ); |
| nanoseconds %= Nanosecond::per(Second) as i32; |
| |
| if seconds > 0 && nanoseconds < 0 { |
| // `seconds` cannot overflow here because it is positive. |
| seconds -= 1; |
| nanoseconds += Nanosecond::per(Second) as i32; |
| } else if seconds < 0 && nanoseconds > 0 { |
| // `seconds` cannot overflow here because it is negative. |
| seconds += 1; |
| nanoseconds -= Nanosecond::per(Second) as i32; |
| } |
| |
| // Safety: `nanoseconds` is in range due to the modulus above. |
| unsafe { Self::new_unchecked(seconds, nanoseconds) } |
| } |
| |
| /// Create a new `Duration` with the provided seconds and nanoseconds. |
| pub(crate) const fn new_ranged(mut seconds: i64, mut nanoseconds: Nanoseconds) -> Self { |
| if seconds > 0 && nanoseconds.get() < 0 { |
| // `seconds` cannot overflow here because it is positive. |
| seconds -= 1; |
| // Safety: `nanoseconds` is negative with a maximum of 999,999,999, so adding a billion |
| // to it is guaranteed to result in an in-range value. |
| nanoseconds = unsafe { |
| Nanoseconds::new_unchecked(nanoseconds.get() + Nanosecond::per(Second) as i32) |
| }; |
| } else if seconds < 0 && nanoseconds.get() > 0 { |
| // `seconds` cannot overflow here because it is negative. |
| seconds += 1; |
| // Safety: `nanoseconds` is positive with a minimum of -999,999,999, so subtracting a |
| // billion from it is guaranteed to result in an in-range value. |
| nanoseconds = unsafe { |
| Nanoseconds::new_unchecked(nanoseconds.get() - Nanosecond::per(Second) as i32) |
| }; |
| } |
| |
| Self::new_ranged_unchecked(seconds, nanoseconds) |
| } |
| |
| /// 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()); |
| /// ``` |
| /// |
| /// # Panics |
| /// |
| /// This may panic if an overflow occurs. |
| pub const fn weeks(weeks: i64) -> Self { |
| Self::seconds(expect_opt!( |
| weeks.checked_mul(Second::per(Week) as _), |
| "overflow constructing `time::Duration`" |
| )) |
| } |
| |
| /// 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()); |
| /// ``` |
| /// |
| /// # Panics |
| /// |
| /// This may panic if an overflow occurs. |
| pub const fn days(days: i64) -> Self { |
| Self::seconds(expect_opt!( |
| days.checked_mul(Second::per(Day) as _), |
| "overflow constructing `time::Duration`" |
| )) |
| } |
| |
| /// 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()); |
| /// ``` |
| /// |
| /// # Panics |
| /// |
| /// This may panic if an overflow occurs. |
| pub const fn hours(hours: i64) -> Self { |
| Self::seconds(expect_opt!( |
| hours.checked_mul(Second::per(Hour) as _), |
| "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()); |
| /// ``` |
| /// |
| /// # Panics |
| /// |
| /// This may panic if an overflow occurs. |
| pub const fn minutes(minutes: i64) -> Self { |
| Self::seconds(expect_opt!( |
| minutes.checked_mul(Second::per(Minute) as _), |
| "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_ranged_unchecked(seconds, Nanoseconds::new_static::<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 { |
| try_from_secs!( |
| secs = seconds, |
| mantissa_bits = 52, |
| exponent_bits = 11, |
| offset = 44, |
| bits_ty = u64, |
| bits_ty_signed = i64, |
| double_ty = u128, |
| float_ty = f64, |
| is_nan = crate::expect_failed("passed NaN to `time::Duration::seconds_f64`"), |
| is_overflow = crate::expect_failed("overflow constructing `time::Duration`"), |
| ) |
| } |
| |
| /// 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 { |
| try_from_secs!( |
| secs = seconds, |
| mantissa_bits = 23, |
| exponent_bits = 8, |
| offset = 41, |
| bits_ty = u32, |
| bits_ty_signed = i32, |
| double_ty = u64, |
| float_ty = f32, |
| is_nan = crate::expect_failed("passed NaN to `time::Duration::seconds_f32`"), |
| is_overflow = crate::expect_failed("overflow constructing `time::Duration`"), |
| ) |
| } |
| |
| /// Creates a new `Duration` from the specified number of seconds |
| /// represented as `f64`. Any values that are out of bounds are saturated at |
| /// the minimum or maximum respectively. `NaN` gets turned into a `Duration` |
| /// of 0 seconds. |
| /// |
| /// ```rust |
| /// # use time::{Duration, ext::NumericalDuration}; |
| /// assert_eq!(Duration::saturating_seconds_f64(0.5), 0.5.seconds()); |
| /// assert_eq!(Duration::saturating_seconds_f64(-0.5), -0.5.seconds()); |
| /// assert_eq!( |
| /// Duration::saturating_seconds_f64(f64::NAN), |
| /// Duration::new(0, 0), |
| /// ); |
| /// assert_eq!( |
| /// Duration::saturating_seconds_f64(f64::NEG_INFINITY), |
| /// Duration::MIN, |
| /// ); |
| /// assert_eq!( |
| /// Duration::saturating_seconds_f64(f64::INFINITY), |
| /// Duration::MAX, |
| /// ); |
| /// ``` |
| pub fn saturating_seconds_f64(seconds: f64) -> Self { |
| try_from_secs!( |
| secs = seconds, |
| mantissa_bits = 52, |
| exponent_bits = 11, |
| offset = 44, |
| bits_ty = u64, |
| bits_ty_signed = i64, |
| double_ty = u128, |
| float_ty = f64, |
| is_nan = return Self::ZERO, |
| is_overflow = return if seconds < 0.0 { Self::MIN } else { Self::MAX }, |
| ) |
| } |
| |
| /// Creates a new `Duration` from the specified number of seconds |
| /// represented as `f32`. Any values that are out of bounds are saturated at |
| /// the minimum or maximum respectively. `NaN` gets turned into a `Duration` |
| /// of 0 seconds. |
| /// |
| /// ```rust |
| /// # use time::{Duration, ext::NumericalDuration}; |
| /// assert_eq!(Duration::saturating_seconds_f32(0.5), 0.5.seconds()); |
| /// assert_eq!(Duration::saturating_seconds_f32(-0.5), (-0.5).seconds()); |
| /// assert_eq!( |
| /// Duration::saturating_seconds_f32(f32::NAN), |
| /// Duration::new(0, 0), |
| /// ); |
| /// assert_eq!( |
| /// Duration::saturating_seconds_f32(f32::NEG_INFINITY), |
| /// Duration::MIN, |
| /// ); |
| /// assert_eq!( |
| /// Duration::saturating_seconds_f32(f32::INFINITY), |
| /// Duration::MAX, |
| /// ); |
| /// ``` |
| pub fn saturating_seconds_f32(seconds: f32) -> Self { |
| try_from_secs!( |
| secs = seconds, |
| mantissa_bits = 23, |
| exponent_bits = 8, |
| offset = 41, |
| bits_ty = u32, |
| bits_ty_signed = i32, |
| double_ty = u64, |
| float_ty = f32, |
| is_nan = return Self::ZERO, |
| is_overflow = return if seconds < 0.0 { Self::MIN } else { Self::MAX }, |
| ) |
| } |
| |
| /// Creates a new `Duration` from the specified number of seconds |
| /// represented as `f64`. Returns `None` if the `Duration` can't be |
| /// represented. |
| /// |
| /// ```rust |
| /// # use time::{Duration, ext::NumericalDuration}; |
| /// assert_eq!(Duration::checked_seconds_f64(0.5), Some(0.5.seconds())); |
| /// assert_eq!(Duration::checked_seconds_f64(-0.5), Some(-0.5.seconds())); |
| /// assert_eq!(Duration::checked_seconds_f64(f64::NAN), None); |
| /// assert_eq!(Duration::checked_seconds_f64(f64::NEG_INFINITY), None); |
| /// assert_eq!(Duration::checked_seconds_f64(f64::INFINITY), None); |
| /// ``` |
| pub fn checked_seconds_f64(seconds: f64) -> Option<Self> { |
| Some(try_from_secs!( |
| secs = seconds, |
| mantissa_bits = 52, |
| exponent_bits = 11, |
| offset = 44, |
| bits_ty = u64, |
| bits_ty_signed = i64, |
| double_ty = u128, |
| float_ty = f64, |
| is_nan = return None, |
| is_overflow = return None, |
| )) |
| } |
| |
| /// Creates a new `Duration` from the specified number of seconds |
| /// represented as `f32`. Returns `None` if the `Duration` can't be |
| /// represented. |
| /// |
| /// ```rust |
| /// # use time::{Duration, ext::NumericalDuration}; |
| /// assert_eq!(Duration::checked_seconds_f32(0.5), Some(0.5.seconds())); |
| /// assert_eq!(Duration::checked_seconds_f32(-0.5), Some(-0.5.seconds())); |
| /// assert_eq!(Duration::checked_seconds_f32(f32::NAN), None); |
| /// assert_eq!(Duration::checked_seconds_f32(f32::NEG_INFINITY), None); |
| /// assert_eq!(Duration::checked_seconds_f32(f32::INFINITY), None); |
| /// ``` |
| pub fn checked_seconds_f32(seconds: f32) -> Option<Self> { |
| Some(try_from_secs!( |
| secs = seconds, |
| mantissa_bits = 23, |
| exponent_bits = 8, |
| offset = 41, |
| bits_ty = u32, |
| bits_ty_signed = i32, |
| double_ty = u64, |
| float_ty = f32, |
| is_nan = return None, |
| is_overflow = return None, |
| )) |
| } |
| |
| /// 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 { |
| // Safety: `nanoseconds` is guaranteed to be in range because of the modulus. |
| unsafe { |
| Self::new_unchecked( |
| milliseconds / Millisecond::per(Second) as i64, |
| (milliseconds % Millisecond::per(Second) as i64 |
| * Nanosecond::per(Millisecond) as i64) as _, |
| ) |
| } |
| } |
| |
| /// 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 { |
| // Safety: `nanoseconds` is guaranteed to be in range because of the modulus. |
| unsafe { |
| Self::new_unchecked( |
| microseconds / Microsecond::per(Second) as i64, |
| (microseconds % Microsecond::per(Second) as i64 |
| * Nanosecond::per(Microsecond) as i64) as _, |
| ) |
| } |
| } |
| |
| /// 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 { |
| // Safety: `nanoseconds` is guaranteed to be in range because of the modulus. |
| unsafe { |
| Self::new_unchecked( |
| nanoseconds / Nanosecond::per(Second) as i64, |
| (nanoseconds % Nanosecond::per(Second) as i64) as _, |
| ) |
| } |
| } |
| |
| /// 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 / Nanosecond::per(Second) as i128; |
| let nanoseconds = nanoseconds % Nanosecond::per(Second) as i128; |
| |
| if seconds > i64::MAX as i128 || seconds < i64::MIN as i128 { |
| crate::expect_failed("overflow constructing `time::Duration`"); |
| } |
| |
| // Safety: `nanoseconds` is guaranteed to be in range because of the modulus above. |
| unsafe { 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() / Second::per(Week) as i64 |
| } |
| |
| /// 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() / Second::per(Day) as i64 |
| } |
| |
| /// 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() / Second::per(Hour) as i64 |
| } |
| |
| /// 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() / Second::per(Minute) as i64 |
| } |
| |
| /// 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.get() as f64 / Nanosecond::per(Second) as f64 |
| } |
| |
| /// 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.get() as f32 / Nanosecond::per(Second) as f32 |
| } |
| |
| /// 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 * Millisecond::per(Second) as i128 |
| + self.nanoseconds.get() as i128 / Nanosecond::per(Millisecond) as i128 |
| } |
| |
| /// Get the number of milliseconds past the number of whole seconds. |
| /// |
| /// Always in the range `-999..=999`. |
| /// |
| /// ```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.get() / Nanosecond::per(Millisecond) as i32) 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 * Microsecond::per(Second) as i128 |
| + self.nanoseconds.get() as i128 / Nanosecond::per(Microsecond) as i128 |
| } |
| |
| /// Get the number of microseconds past the number of whole seconds. |
| /// |
| /// Always in the range `-999_999..=999_999`. |
| /// |
| /// ```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.get() / Nanosecond::per(Microsecond) as i32 |
| } |
| |
| /// 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 * Nanosecond::per(Second) as i128 + self.nanoseconds.get() as i128 |
| } |
| |
| /// Get the number of nanoseconds past the number of whole seconds. |
| /// |
| /// The returned value will always be in the range `-999_999_999..=999_999_999`. |
| /// |
| /// ```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.get() |
| } |
| |
| /// Get the number of nanoseconds past the number of whole seconds. |
| #[cfg(feature = "quickcheck")] |
| pub(crate) const fn subsec_nanoseconds_ranged(self) -> Nanoseconds { |
| 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.get() + rhs.nanoseconds.get(); |
| |
| if nanoseconds >= Nanosecond::per(Second) as _ || seconds < 0 && nanoseconds > 0 { |
| nanoseconds -= Nanosecond::per(Second) as i32; |
| seconds = const_try_opt!(seconds.checked_add(1)); |
| } else if nanoseconds <= -(Nanosecond::per(Second) as i32) || seconds > 0 && nanoseconds < 0 |
| { |
| nanoseconds += Nanosecond::per(Second) as i32; |
| seconds = const_try_opt!(seconds.checked_sub(1)); |
| } |
| |
| // Safety: `nanoseconds` is guaranteed to be in range because of the overflow handling. |
| unsafe { Some(Self::new_unchecked(seconds, nanoseconds)) } |
| } |
| |
| /// 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.get() - rhs.nanoseconds.get(); |
| |
| if nanoseconds >= Nanosecond::per(Second) as _ || seconds < 0 && nanoseconds > 0 { |
| nanoseconds -= Nanosecond::per(Second) as i32; |
| seconds = const_try_opt!(seconds.checked_add(1)); |
| } else if nanoseconds <= -(Nanosecond::per(Second) as i32) || seconds > 0 && nanoseconds < 0 |
| { |
| nanoseconds += Nanosecond::per(Second) as i32; |
| seconds = const_try_opt!(seconds.checked_sub(1)); |
| } |
| |
| // Safety: `nanoseconds` is guaranteed to be in range because of the overflow handling. |
| unsafe { Some(Self::new_unchecked(seconds, nanoseconds)) } |
| } |
| |
| /// 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.get() as i64 * rhs as i64; |
| let extra_secs = total_nanos / Nanosecond::per(Second) as i64; |
| let nanoseconds = (total_nanos % Nanosecond::per(Second) as i64) as _; |
| let seconds = const_try_opt!( |
| const_try_opt!(self.seconds.checked_mul(rhs as _)).checked_add(extra_secs) |
| ); |
| |
| // Safety: `nanoseconds` is guaranteed to be in range because of the modulus above. |
| unsafe { Some(Self::new_unchecked(seconds, nanoseconds)) } |
| } |
| |
| /// 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 (secs, extra_secs) = ( |
| const_try_opt!(self.seconds.checked_div(rhs as i64)), |
| self.seconds % (rhs as i64), |
| ); |
| let (mut nanos, extra_nanos) = (self.nanoseconds.get() / rhs, self.nanoseconds.get() % rhs); |
| nanos += ((extra_secs * (Nanosecond::per(Second) as i64) + extra_nanos as i64) |
| / (rhs as i64)) as i32; |
| |
| // Safety: `nanoseconds` is in range. |
| unsafe { Some(Self::new_unchecked(secs, nanos)) } |
| } |
| |
| /// Computes `-self`, returning `None` if the result would overflow. |
| /// |
| /// ```rust |
| /// # use time::ext::NumericalDuration; |
| /// # use time::Duration; |
| /// assert_eq!(5.seconds().checked_neg(), Some((-5).seconds())); |
| /// assert_eq!(Duration::MIN.checked_neg(), None); |
| /// ``` |
| pub const fn checked_neg(self) -> Option<Self> { |
| if self.seconds == i64::MIN { |
| None |
| } else { |
| Some(Self::new_ranged_unchecked( |
| -self.seconds, |
| self.nanoseconds.neg(), |
| )) |
| } |
| } |
| // 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.get() + rhs.nanoseconds.get(); |
| |
| if nanoseconds >= Nanosecond::per(Second) as _ || seconds < 0 && nanoseconds > 0 { |
| nanoseconds -= Nanosecond::per(Second) as i32; |
| seconds = match seconds.checked_add(1) { |
| Some(seconds) => seconds, |
| None => return Self::MAX, |
| }; |
| } else if nanoseconds <= -(Nanosecond::per(Second) as i32) || seconds > 0 && nanoseconds < 0 |
| { |
| nanoseconds += Nanosecond::per(Second) as i32; |
| seconds = match seconds.checked_sub(1) { |
| Some(seconds) => seconds, |
| None => return Self::MIN, |
| }; |
| } |
| |
| // Safety: `nanoseconds` is guaranteed to be in range because of the overflow handling. |
| unsafe { Self::new_unchecked(seconds, nanoseconds) } |
| } |
| |
| /// Computes `self - rhs`, saturating if an overflow occurred. |
| /// |
| /// ```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 mut nanoseconds = self.nanoseconds.get() - rhs.nanoseconds.get(); |
| |
| if nanoseconds >= Nanosecond::per(Second) as _ || seconds < 0 && nanoseconds > 0 { |
| nanoseconds -= Nanosecond::per(Second) as i32; |
| seconds = match seconds.checked_add(1) { |
| Some(seconds) => seconds, |
| None => return Self::MAX, |
| }; |
| } else if nanoseconds <= -(Nanosecond::per(Second) as i32) || seconds > 0 && nanoseconds < 0 |
| { |
| nanoseconds += Nanosecond::per(Second) as i32; |
| seconds = match seconds.checked_sub(1) { |
| Some(seconds) => seconds, |
| None => return Self::MIN, |
| }; |
| } |
| |
| // Safety: `nanoseconds` is guaranteed to be in range because of the overflow handling. |
| unsafe { Self::new_unchecked(seconds, nanoseconds) } |
| } |
| |
| /// Computes `self * rhs`, saturating if an overflow occurred. |
| /// |
| /// ```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.get() as i64 * rhs as i64; |
| let extra_secs = total_nanos / Nanosecond::per(Second) as i64; |
| let nanoseconds = (total_nanos % Nanosecond::per(Second) as i64) 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; |
| } |
| let (seconds, overflow2) = seconds.overflowing_add(extra_secs); |
| if overflow2 { |
| if self.seconds > 0 && rhs > 0 { |
| return Self::MAX; |
| } |
| return Self::MIN; |
| } |
| |
| // Safety: `nanoseconds` is guaranteed to be in range because of to the modulus above. |
| unsafe { 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")] |
| #[deprecated( |
| since = "0.3.32", |
| note = "extremely limited use case, not intended for benchmarking" |
| )] |
| #[allow(deprecated)] |
| pub fn time_fn<T>(f: impl FnOnce() -> T) -> (Self, T) { |
| let start = Instant::now(); |
| let return_value = f(); |
| let end = Instant::now(); |
| |
| (end - start, return_value) |
| } |
| } |
| |
| // 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. |
| |
| 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 / Second::per(Day) as f64); |
| item!("h", seconds / Second::per(Hour) as f64); |
| item!("m", seconds / Second::per(Minute) as f64); |
| item!("s", seconds); |
| item!("ms", seconds * Millisecond::per(Second) as f64); |
| item!("µs", seconds * Microsecond::per(Second) as f64); |
| item!("ns", seconds * Nanosecond::per(Second) as f64); |
| } else { |
| // 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.get().unsigned_abs(); |
| |
| item!("d", seconds / Second::per(Day).extend::<u64>())?; |
| item!( |
| "h", |
| seconds / Second::per(Hour).extend::<u64>() % Hour::per(Day).extend::<u64>() |
| )?; |
| item!( |
| "m", |
| seconds / Second::per(Minute).extend::<u64>() % Minute::per(Hour).extend::<u64>() |
| )?; |
| item!("s", seconds % Second::per(Minute).extend::<u64>())?; |
| item!("ms", nanoseconds / Nanosecond::per(Millisecond))?; |
| item!( |
| "µs", |
| nanoseconds / Nanosecond::per(Microsecond).extend::<u32>() |
| % Microsecond::per(Millisecond).extend::<u32>() |
| )?; |
| item!( |
| "ns", |
| nanoseconds % Nanosecond::per(Microsecond).extend::<u32>() |
| )?; |
| } |
| |
| 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().cast_signed(), |
| )) |
| } |
| } |
| |
| 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 |
| .get() |
| .try_into() |
| .map_err(|_| error::ConversionRange)?, |
| )) |
| } |
| } |
| |
| impl Add for Duration { |
| type Output = Self; |
| |
| /// # Panics |
| /// |
| /// This may panic if an overflow occurs. |
| fn add(self, rhs: Self) -> Self::Output { |
| self.checked_add(rhs) |
| .expect("overflow when adding durations") |
| } |
| } |
| |
| impl Add<StdDuration> for Duration { |
| type Output = Self; |
| |
| /// # Panics |
| /// |
| /// This may panic if an overflow occurs. |
| 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) -> Self::Output { |
| rhs + self |
| } |
| } |
| |
| impl_add_assign!(Duration: Self, StdDuration); |
| |
| impl AddAssign<Duration> for StdDuration { |
| /// # Panics |
| /// |
| /// This may panic if the resulting addition cannot be represented. |
| 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.checked_neg().expect("overflow when negating duration") |
| } |
| } |
| |
| impl Sub for Duration { |
| type Output = Self; |
| |
| /// # Panics |
| /// |
| /// This may panic if an overflow occurs. |
| fn sub(self, rhs: Self) -> Self::Output { |
| self.checked_sub(rhs) |
| .expect("overflow when subtracting durations") |
| } |
| } |
| |
| impl Sub<StdDuration> for Duration { |
| type Output = Self; |
| |
| /// # Panics |
| /// |
| /// This may panic if an overflow occurs. |
| 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; |
| |
| /// # Panics |
| /// |
| /// This may panic if an overflow occurs. |
| 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 { |
| /// # Panics |
| /// |
| /// This may panic if the resulting subtraction can not be represented. |
| 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.cast_signed().extend::<i128>()) |
| .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.cast_signed().extend::<i128>() |
| ) |
| } |
| } |
| )+}; |
| } |
| 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<Duration> for f32 { |
| type Output = Duration; |
| |
| 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 Mul<Duration> for f64 { |
| type Output = Duration; |
| |
| fn mul(self, rhs: Duration) -> Self::Output { |
| rhs * self |
| } |
| } |
| |
| impl_mul_assign!(Duration: i8, i16, i32, u8, u16, u32, f32, f64); |
| |
| impl Div<f32> for Duration { |
| type Output = Self; |
| |
| fn div(self, rhs: f32) -> Self::Output { |
| Self::seconds_f32(self.as_seconds_f32() / rhs) |
| } |
| } |
| |
| 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.cast_unsigned() { |
| return Some(Ordering::Less); |
| } |
| |
| Some( |
| self.seconds |
| .cmp(&rhs.as_secs().cast_signed()) |
| .then_with(|| { |
| self.nanoseconds |
| .get() |
| .cmp(&rhs.subsec_nanos().cast_signed()) |
| }), |
| ) |
| } |
| } |
| |
| 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 |