time/ext/
systemtime.rs

1use std::time::SystemTime;
2
3use crate::Duration;
4
5/// Sealed trait to prevent downstream implementations.
6mod sealed {
7    /// A trait that cannot be implemented by downstream users.
8    pub trait Sealed: Sized {}
9    impl Sealed for std::time::SystemTime {}
10}
11
12/// An extension trait for [`std::time::SystemTime`] that adds methods for
13/// [`time::Duration`](Duration)s.
14pub trait SystemTimeExt: sealed::Sealed {
15    /// Adds the given [`Duration`] to the [`SystemTime`], returning `None` is the result cannot be
16    /// represented by the underlying data structure.
17    fn checked_add_signed(&self, duration: Duration) -> Option<Self>;
18
19    /// Subtracts the given [`Duration`] from the [`SystemTime`], returning `None` is the result
20    /// cannot be represented by the underlying data structure.
21    fn checked_sub_signed(&self, duration: Duration) -> Option<Self>;
22
23    /// Returns the amount of time elapsed from another [`SystemTime`] to this one. This will be
24    /// negative if `earlier` is later than `self.`
25    ///
26    /// If the duration cannot be stored by [`Duration`], the value will be saturated to
27    /// [`Duration::MIN`] or [`Duration::MAX`] as appropriate.
28    ///
29    /// # Example
30    ///
31    /// ```rust
32    /// # use std::time::SystemTime;
33    /// # use time::ext::{NumericalDuration, SystemTimeExt};
34    /// let epoch = SystemTime::UNIX_EPOCH;
35    /// let other = epoch + 1.seconds();
36    /// assert_eq!(other.signed_duration_since(epoch), 1.seconds());
37    /// assert_eq!(epoch.signed_duration_since(other), (-1).seconds());
38    /// ```
39    fn signed_duration_since(&self, earlier: Self) -> Duration;
40}
41
42impl SystemTimeExt for SystemTime {
43    #[inline]
44    fn checked_add_signed(&self, duration: Duration) -> Option<Self> {
45        if duration.is_positive() {
46            self.checked_add(duration.unsigned_abs())
47        } else if duration.is_negative() {
48            self.checked_sub(duration.unsigned_abs())
49        } else {
50            Some(*self)
51        }
52    }
53
54    #[inline]
55    fn checked_sub_signed(&self, duration: Duration) -> Option<Self> {
56        if duration.is_positive() {
57            self.checked_sub(duration.unsigned_abs())
58        } else if duration.is_negative() {
59            self.checked_add(duration.unsigned_abs())
60        } else {
61            Some(*self)
62        }
63    }
64
65    #[inline]
66    fn signed_duration_since(&self, earlier: Self) -> Duration {
67        match self.duration_since(earlier) {
68            Ok(duration) => duration.try_into().unwrap_or(Duration::MAX),
69            Err(err) => {
70                let seconds = match i64::try_from(err.duration().as_secs()) {
71                    Ok(seconds) => -seconds,
72                    Err(_) => return Duration::MIN,
73                };
74                let nanoseconds = -err.duration().subsec_nanos().cast_signed();
75
76                // Safety: `nanoseconds` is guaranteed to be between -999_999_999 and 0
77                // inclusive.
78                unsafe { Duration::new_unchecked(seconds, nanoseconds) }
79            }
80        }
81    }
82}