//! 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, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; use core::time::Duration as StdDuration; #[cfg(feature = "std")] use std::time::SystemTime;
use deranged::RangedI32; use num_conv::prelude::*;
/// 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_t::<i32>(Second) + 1 }, { Nanosecond::per_t::<i32>(Second) - 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)] pubstruct 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,
padding: Padding,
}
/// 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,
) => {{ '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)
} elseif exp < 0 { // the input is less than 1 second let t = (mant as $double_ty) << ($offset + exp); let nanos_offset = $mant_bits + $offset; #[allow(trivial_numeric_casts)] let nanos_tmp = Nanosecond::per_t::<u128>(Second) * t as u128; 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_t::<u32>(Second)) {
(0, nanos)
} else {
(1, 0)
}
} elseif exp < $mant_bits { #[allow(trivial_numeric_casts)] let secs = (mant >> ($mant_bits - exp)) as u64; let t = ((mant << exp) & MANT_MASK) as $double_ty; let nanos_offset = $mant_bits; let nanos_tmp = Nanosecond::per_t::<$double_ty>(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_t::<u32>(Second)) {
(secs, nanos)
} else {
(secs + 1, 0)
}
} elseif 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 #[allow(trivial_numeric_casts)] let secs = (mant as u64) << (exp - $mant_bits);
(secs, 0)
} elseif 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 Ok(Self::new_ranged_unchecked(i64::MIN, Nanoseconds::new_static::<0>()));
} elseif $secs.is_nan() { // Change from std: std doesn't differentiate between the error // cases. break'value Err(FloatConstructorError::Nan);
} elseif $secs.is_sign_negative() { break'value Err(FloatConstructorError::NegOverflow);
} else { break'value Err(FloatConstructorError::PosOverflow);
};
// 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.
Ok(unsafe { Self::new_unchecked(secs_signed, nanos_signed) })
}
}};
}
/// The minimum possible duration. Adding any negative duration to this will cause an overflow. pubconst MIN: Self = Self::new_ranged(i64::MIN, Nanoseconds::MIN);
/// The maximum possible duration. Adding any positive duration to this will cause an overflow. pubconst MAX: Self = Self::new_ranged(i64::MAX, Nanoseconds::MAX);
/// Check if a duration is exactly zero. /// /// ```rust /// # use time::ext::NumericalDuration; /// assert!(0.seconds().is_zero()); /// assert!(!1.nanoseconds().is_zero()); /// ``` #[inline] pubconstfn 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()); /// ``` #[inline] pubconstfn 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()); /// ``` #[inline] pubconstfn is_positive(self) -> bool { self.seconds > 0 || self.nanoseconds.get() > 0
}
/// 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()); /// ``` #[inline] pubconstfn abs(self) -> Self { matchself.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()); /// ``` #[inline] pubconstfn unsigned_abs(self) -> StdDuration {
StdDuration::new( self.seconds.unsigned_abs(), self.nanoseconds.get().unsigned_abs(),
)
}
/// 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. #[inline] #[track_caller] pub(crate) constunsafefn 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. #[inline] #[track_caller] pub(crate) constfn new_ranged_unchecked(seconds: i64, nanoseconds: Nanoseconds) -> Self { if seconds < 0 {
debug_assert!(nanoseconds.get() <= 0);
} elseif seconds > 0 {
debug_assert!(nanoseconds.get() >= 0);
}
/// 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. #[inline] #[track_caller] pubconstfn new(mut seconds: i64, mut nanoseconds: i32) -> Self {
seconds = seconds
.checked_add(nanoseconds as i64 / Nanosecond::per_t::<i64>(Second))
.expect("overflow constructing `time::Duration`");
nanoseconds %= Nanosecond::per_t::<i32>(Second);
if seconds > 0 && nanoseconds < 0 { // `seconds` cannot overflow here because it is positive.
seconds -= 1;
nanoseconds += Nanosecond::per_t::<i32>(Second);
} elseif seconds < 0 && nanoseconds > 0 { // `seconds` cannot overflow here because it is negative.
seconds += 1;
nanoseconds -= Nanosecond::per_t::<i32>(Second);
}
// 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. #[inline] pub(crate) constfn 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_t::<i32>(Second))
};
} elseif 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_t::<i32>(Second))
};
}
/// 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. #[inline] #[track_caller] pubconstfn weeks(weeks: i64) -> Self { Self::seconds(
weeks
.checked_mul(Second::per_t(Week))
.expect("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. #[inline] #[track_caller] pubconstfn days(days: i64) -> Self { Self::seconds(
days.checked_mul(Second::per_t(Day))
.expect("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. #[inline] #[track_caller] pubconstfn hours(hours: i64) -> Self { Self::seconds(
hours
.checked_mul(Second::per_t(Hour))
.expect("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. #[inline] #[track_caller] pubconstfn minutes(minutes: i64) -> Self { Self::seconds(
minutes
.checked_mul(Second::per_t(Minute))
.expect("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()); /// ``` #[inline] pubconstfn seconds(seconds: i64) -> Self { Self::new_ranged_unchecked(seconds, Nanoseconds::new_static::<0>())
}
/// Create a new `Duration` from the specified number of seconds represented as `f64`. /// /// If the value is `NaN` or out of bounds, an error is returned that can be handled in the /// desired manner by the caller. #[inline] constfn try_seconds_f64(seconds: f64) -> Result<Self, FloatConstructorError> {
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,
)
}
/// Create a new `Duration` from the specified number of seconds represented as `f32`. /// /// If the value is `NaN` or out of bounds, an error is returned that can be handled in the /// desired manner by the caller. #[inline] constfn try_seconds_f32(seconds: f32) -> Result<Self, FloatConstructorError> {
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,
)
}
/// 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()); /// ``` #[inline] #[track_caller] pubconstfn seconds_f64(seconds: f64) -> Self { matchSelf::try_seconds_f64(seconds) {
Ok(duration) => duration,
Err(FloatConstructorError::Nan) => {
panic!("passed NaN to `time::Duration::seconds_f64`");
}
Err(FloatConstructorError::NegOverflow | FloatConstructorError::PosOverflow) => {
panic!("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()); /// ``` #[inline] #[track_caller] pubconstfn seconds_f32(seconds: f32) -> Self { matchSelf::try_seconds_f32(seconds) {
Ok(duration) => duration,
Err(FloatConstructorError::Nan) => {
panic!("passed NaN to `time::Duration::seconds_f32`");
}
Err(FloatConstructorError::NegOverflow | FloatConstructorError::PosOverflow) => {
panic!("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, /// ); /// ``` #[inline] pubconstfn saturating_seconds_f64(seconds: f64) -> Self { matchSelf::try_seconds_f64(seconds) {
Ok(duration) => duration,
Err(FloatConstructorError::Nan) => Self::ZERO,
Err(FloatConstructorError::NegOverflow) => Self::MIN,
Err(FloatConstructorError::PosOverflow) => 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, /// ); /// ``` #[inline] pubconstfn saturating_seconds_f32(seconds: f32) -> Self { matchSelf::try_seconds_f32(seconds) {
Ok(duration) => duration,
Err(FloatConstructorError::Nan) => Self::ZERO,
Err(FloatConstructorError::NegOverflow) => Self::MIN,
Err(FloatConstructorError::PosOverflow) => 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); /// ``` #[inline] pubconstfn checked_seconds_f64(seconds: f64) -> Option<Self> { matchSelf::try_seconds_f64(seconds) {
Ok(duration) => Some(duration),
Err(_) => 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); /// ``` #[inline] pubconstfn checked_seconds_f32(seconds: f32) -> Option<Self> { matchSelf::try_seconds_f32(seconds) {
Ok(duration) => Some(duration),
Err(_) => 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()); /// ``` #[inline] pubconstfn milliseconds(milliseconds: i64) -> Self { // Safety: `nanoseconds` is guaranteed to be in range because of the modulus. unsafe { Self::new_unchecked(
milliseconds / Millisecond::per_t::<i64>(Second),
(milliseconds % Millisecond::per_t::<i64>(Second)
* Nanosecond::per_t::<i64>(Millisecond)) as i32,
)
}
}
/// 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()); /// ``` #[inline] pubconstfn microseconds(microseconds: i64) -> Self { // Safety: `nanoseconds` is guaranteed to be in range because of the modulus. unsafe { Self::new_unchecked(
microseconds / Microsecond::per_t::<i64>(Second),
(microseconds % Microsecond::per_t::<i64>(Second)
* Nanosecond::per_t::<i64>(Microsecond)) as i32,
)
}
}
/// 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); /// ``` #[inline] pubconstfn nanoseconds(nanoseconds: i64) -> Self { // Safety: `nanoseconds` is guaranteed to be in range because of the modulus. unsafe { Self::new_unchecked(
nanoseconds / Nanosecond::per_t::<i64>(Second),
(nanoseconds % Nanosecond::per_t::<i64>(Second)) as i32,
)
}
}
/// Create a new `Duration` with the given number of nanoseconds. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!( /// Duration::nanoseconds_i128(1_234_567_890), /// 1.seconds() + 234_567_890.nanoseconds() /// ); /// ``` /// /// # Panics /// /// This may panic if an overflow occurs. This may happen because the input range cannot be /// fully mapped to the output. #[inline] #[track_caller] pubconstfn nanoseconds_i128(nanoseconds: i128) -> Self { let seconds = nanoseconds / Nanosecond::per_t::<i128>(Second); let nanoseconds = nanoseconds % Nanosecond::per_t::<i128>(Second);
if seconds > i64::MAX as i128 || seconds < i64::MIN as i128 {
panic!("overflow constructing `time::Duration`");
}
// Safety: `nanoseconds` is guaranteed to be in range because of the modulus above. unsafe { Self::new_unchecked(seconds as i64, nanoseconds as i32) }
}
/// 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); /// ``` #[inline] pubconstfn whole_weeks(self) -> i64 { self.whole_seconds() / Second::per_t::<i64>(Week)
}
/// 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); /// ``` #[inline] pubconstfn whole_days(self) -> i64 { self.whole_seconds() / Second::per_t::<i64>(Day)
}
/// 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); /// ``` #[inline] pubconstfn whole_hours(self) -> i64 { self.whole_seconds() / Second::per_t::<i64>(Hour)
}
/// 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); /// ``` #[inline] pubconstfn whole_minutes(self) -> i64 { self.whole_seconds() / Second::per_t::<i64>(Minute)
}
/// 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); /// ``` #[inline] pubconstfn 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); /// ``` #[inline] pubconstfn as_seconds_f64(self) -> f64 { self.seconds as f64 + self.nanoseconds.get() as f64 / Nanosecond::per_t::<f64>(Second)
}
/// 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); /// ``` #[inline] pubconstfn as_seconds_f32(self) -> f32 { self.seconds as f32 + self.nanoseconds.get() as f32 / Nanosecond::per_t::<f32>(Second)
}
/// 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); /// ``` #[inline] pubconstfn whole_milliseconds(self) -> i128 { self.seconds as i128 * Millisecond::per_t::<i128>(Second)
+ self.nanoseconds.get() as i128 / Nanosecond::per_t::<i128>(Millisecond)
}
/// 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); /// ``` #[inline] pubconstfn subsec_milliseconds(self) -> i16 {
(self.nanoseconds.get() / Nanosecond::per_t::<i32>(Millisecond)) as i16
}
/// 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); /// ``` #[inline] pubconstfn whole_microseconds(self) -> i128 { self.seconds as i128 * Microsecond::per_t::<i128>(Second)
+ self.nanoseconds.get() as i128 / Nanosecond::per_t::<i128>(Microsecond)
}
/// 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); /// ``` #[inline] pubconstfn subsec_microseconds(self) -> i32 { self.nanoseconds.get() / Nanosecond::per_t::<i32>(Microsecond)
}
/// 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); /// ``` #[inline] pubconstfn whole_nanoseconds(self) -> i128 { self.seconds as i128 * Nanosecond::per_t::<i128>(Second) + 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); /// ``` #[inline] pubconstfn subsec_nanoseconds(self) -> i32 { self.nanoseconds.get()
}
/// Get the number of nanoseconds past the number of whole seconds. #[cfg(feature = "quickcheck")] #[inline] pub(crate) constfn subsec_nanoseconds_ranged(self) -> Nanoseconds { self.nanoseconds
}
// 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); /// ``` #[inline] pubconstfn 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_t::<i64>(Second); let nanoseconds = (total_nanos % Nanosecond::per_t::<i64>(Second)) as i32; let (seconds, overflow1) = self.seconds.overflowing_mul(rhs as i64); if overflow1 { ifself.seconds > 0 && rhs > 0 || self.seconds < 0 && rhs < 0 { returnSelf::MAX;
} returnSelf::MIN;
} let (seconds, overflow2) = seconds.overflowing_add(extra_secs); if overflow2 { ifself.seconds > 0 && rhs > 0 { returnSelf::MAX;
} returnSelf::MIN;
}
// Safety: `nanoseconds` is guaranteed to be in range because of to the modulus above. unsafe { Self::new_unchecked(seconds, nanoseconds) }
}
/// 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")] #[doc(hidden)] #[inline] #[track_caller] #[deprecated(
since = "0.3.32",
note = "extremely limited use case, not intended for benchmarking"
)] #[expect(deprecated)] pubfn 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)
}
}
/// 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 { ifself.is_negative() {
f.write_str("-")?;
}
ifself.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();
/// 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();
/// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] 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. #[inline] #[track_caller] 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;
/// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn add(self, rhs: Duration) -> Self::Output {
rhs + self
}
}
impl AddAssign<Self> for Duration { /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn add_assign(&mutself, rhs: Self) {
*self = *self + rhs;
}
}
impl AddAssign<StdDuration> for Duration { /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn add_assign(&mutself, rhs: StdDuration) {
*self = *self + rhs;
}
}
impl AddAssign<Duration> for StdDuration { /// # Panics /// /// This may panic if the resulting addition cannot be represented. #[inline] #[track_caller] fn add_assign(&mutself, 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;
/// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] 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. #[inline] #[track_caller] 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. #[inline] #[track_caller] 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. #[inline] #[track_caller] fn sub(self, rhs: Duration) -> Self::Output {
Duration::try_from(self)
.expect("overflow converting `std::time::Duration` to `time::Duration`")
- rhs
}
}
impl SubAssign<Self> for Duration { /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn sub_assign(&mutself, rhs: Self) {
*self = *self - rhs;
}
}
impl SubAssign<StdDuration> for Duration { /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn sub_assign(&mutself, rhs: StdDuration) {
*self = *self - rhs;
}
}
impl SubAssign<Duration> for StdDuration { /// # Panics /// /// This may panic if the resulting subtraction can not be represented. #[inline] #[track_caller] fn sub_assign(&mutself, 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.",
);
}
}
/// Given a value and whether it is signed, cast it to the signed version.
macro_rules! cast_signed {
(@signed $val:ident) => {
$val
};
(@unsigned $val:ident) => {
$val.cast_signed()
};
}
/// Implement `Mul` (reflexively), `MulAssign`, `Div`, and `DivAssign` for `Duration` for various /// signed types.
macro_rules! duration_mul_div_int {
($(@$signedness:ident $type:ty),+ $(,)?) => {$( impl Mul<$type> for Duration { type Output = Self;
/// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn mul(self, rhs: $type) -> Self::Output { Self::nanoseconds_i128( self.whole_nanoseconds()
.checked_mul(cast_signed!(@$signedness rhs).extend::<i128>())
.expect("overflow when multiplying duration")
)
}
}
impl Mul<Duration> for $type { type Output = Duration;
/// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn mul(self, rhs: Duration) -> Self::Output {
rhs * self
}
}
impl MulAssign<$type> for Duration { /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn mul_assign(&mutself, rhs: $type) {
*self = *self * rhs;
}
}
impl Div<$type> for Duration { type Output = Self;
/// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn div(self, rhs: $type) -> Self::Output { Self::nanoseconds_i128( self.whole_nanoseconds() / cast_signed!(@$signedness rhs).extend::<i128>()
)
}
}
impl DivAssign<$type> for Duration { /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn div_assign(&mutself, rhs: $type) {
*self = *self / rhs;
}
}
)+};
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.