time/ext/
systemtime.rs

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