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}