Skip to main content

saturating_time/
lib.rs

1#![doc = include_str!("../README.md")]
2#![forbid(unsafe_code)]
3
4use std::time::{Duration, Instant, SystemTime};
5
6mod internal;
7
8/// The core trait of this crait, [`SaturatingTime`].
9///
10/// This trait provides methods for performing saturating arithmetic on those
11/// types in [`std::time`] that not already come with such a functionality,
12/// such as [`SystemTime`] or [`Instant`].
13///
14/// The trait itself is not implementable from the outside, because it is sealed
15/// by an internal trait.
16///
17/// See the methods or the top-level documentation for concrete code examples.
18pub trait SaturatingTime: internal::SaturatingTime {
19    /// Returns the maximum value for this type on the current platform.
20    ///
21    /// This limit is highly platform specific.  It differs heavily between
22    /// Unix, Windows, and other operating systems.
23    ///
24    /// The limit itself is calculated dynamically during runtime with a correct
25    /// algorithm.  Afterwards, it gets stored in a lazy static value, meaning
26    /// that only the first call to it will be slightly more expensive, whereas
27    /// all latter calls will result in an immediate return of the value.
28    ///
29    /// # Examples
30    ///
31    /// ```
32    /// use std::time::{Duration, SystemTime};
33    /// use saturating_time::SaturatingTime;
34    ///
35    /// let max = SystemTime::max_value();
36    ///
37    /// // Adding zero to the maximum value will change nothing.
38    /// assert!(max.checked_add(Duration::ZERO).is_some());
39    ///
40    /// // Adding 1ns to the maximum value will fail.
41    /// assert!(max.checked_add(Duration::new(0, 1)).is_none());
42    ///
43    /// // Subtracting 1ns from the maximum value will work of course.
44    /// assert!(max.checked_sub(Duration::new(0, 1)).is_some());
45    /// ```
46    fn max_value() -> Self {
47        internal::SaturatingTime::max_value()
48    }
49
50    /// Returns the minimum value for this type on the current platform.
51    ///
52    /// This limit is highly platform specific.  It differs heavily between
53    /// Unix, Windows, and other operating systems.
54    ///
55    /// The limit itself is calculated dynamically during runtime with a correct
56    /// algorithm.  Afterwards, it gets stored in a lazy static value, meaning
57    /// that only the first call to it will be slightly more expensive, whereas
58    /// all latter calls will result in an immediate return of the value.
59    ///
60    /// # Examples
61    ///
62    /// ```
63    /// use std::time::{Duration, SystemTime};
64    /// use saturating_time::SaturatingTime;
65    ///
66    /// let min = SystemTime::min_value();
67    ///
68    /// // Subtracting a zero from the minimum value will change nothing.
69    /// assert!(min.checked_sub(Duration::ZERO).is_some());
70    ///
71    /// // Subtracting 1ns from the minimum value will fail.
72    /// assert!(min.checked_sub(Duration::new(0, 1)).is_none());
73    ///
74    /// // Adding 1ns to the minimum value will work of course.
75    /// assert!(min.checked_add(Duration::new(0, 1)).is_some());
76    /// ```
77    fn min_value() -> Self {
78        internal::SaturatingTime::min_value()
79    }
80
81    /// Performs a saturating addition of a [`Duration`].
82    ///
83    /// The resulting value will saturate to [`SaturatingTime::max_value()`] in
84    /// the case the addition would have caused an overflow of value.
85    ///
86    /// # Examples
87    ///
88    /// ```
89    /// use std::time::{Duration, SystemTime};
90    /// use saturating_time::SaturatingTime;
91    ///
92    /// let max = SystemTime::max_value();
93    ///
94    /// // Adding zero will change nothing.
95    /// assert_eq!(max.saturating_add(Duration::ZERO), max);
96    ///
97    /// // Adding 1ns would overflow so we saturate to the maximum.
98    /// assert_eq!(max.saturating_add(Duration::new(0, 1)), max);
99    /// ```
100    fn saturating_add(self, duration: Duration) -> Self {
101        self.checked_add(duration)
102            .unwrap_or(SaturatingTime::max_value())
103    }
104
105    /// Performs a saturating subtraction of a [`Duration`].
106    ///
107    /// The resulting value will saturate to [`SaturatingTime::min_value()`] in
108    /// the case the subtraction would have caused an overflow of value.
109    ///
110    /// # Examples
111    ///
112    /// ```
113    /// use std::time::{Duration, SystemTime};
114    /// use saturating_time::SaturatingTime;
115    ///
116    /// let min = SystemTime::min_value();
117    ///
118    /// // Subtracting zero will change nothing.
119    /// assert_eq!(min.saturating_sub(Duration::ZERO), min);
120    ///
121    /// // Subtracting 1ns would overflow so we saturate to the minimum.
122    /// assert_eq!(min.saturating_sub(Duration::new(0, 1)), min);
123    /// ```
124    fn saturating_sub(self, duration: Duration) -> Self {
125        self.checked_sub(duration)
126            .unwrap_or(SaturatingTime::min_value())
127    }
128
129    /// Performs a saturating time difference calculation between two points.
130    ///
131    /// The resulting value will saturate to [`Duration::ZERO`] in the case that
132    /// the `earlier` point in time is actually not earlier, thereby resulting
133    /// in a negative difference.
134    ///
135    /// # Examples
136    ///
137    /// ```
138    /// use std::time::{Duration, SystemTime};
139    /// use saturating_time::SaturatingTime;
140    ///
141    /// let epoch = SystemTime::UNIX_EPOCH;
142    /// let now = SystemTime::now();
143    /// let min = SystemTime::min_value();
144    ///
145    /// assert!(now.saturating_duration_since(epoch).as_secs() > 0);
146    /// assert!(epoch.saturating_duration_since(epoch) == Duration::ZERO);
147    /// assert!(min.saturating_duration_since(epoch) == Duration::ZERO);
148    /// ```
149    fn saturating_duration_since(&self, earlier: Self) -> Duration {
150        self.checked_duration_since(earlier)
151            .unwrap_or(Duration::ZERO)
152    }
153}
154
155impl SaturatingTime for SystemTime {}
156
157impl SaturatingTime for Instant {
158    // Override to use the provided implementation from the standard library.
159    fn saturating_duration_since(&self, earlier: Self) -> Duration {
160        Self::saturating_duration_since(self, earlier)
161    }
162}
163
164#[cfg(test)]
165mod tests {
166    use super::*;
167    use crate::internal;
168    use std::{
169        fmt::Debug,
170        ops::{Add, Sub},
171        time::{Instant, SystemTime},
172    };
173
174    /// Verifies the maximum and minimum values of [`SaturatingTime`] equal
175    /// their pedant in [`internal::SaturatingTime`].
176    fn min_max<T: SaturatingTime + PartialEq + Debug>() {
177        assert_eq!(
178            <T as SaturatingTime>::max_value(),
179            <T as internal::SaturatingTime>::max_value()
180        );
181        assert_eq!(
182            <T as SaturatingTime>::min_value(),
183            <T as internal::SaturatingTime>::min_value()
184        );
185    }
186
187    /// Verifies the saturating arithmetic for [`SaturatingTime`].
188    fn saturating_add_sub<
189        T: SaturatingTime + PartialEq + Debug + Add<Duration, Output = T> + Sub<Duration, Output = T>,
190    >() {
191        let max = <T as SaturatingTime>::max_value();
192        assert_eq!(max.saturating_add(Duration::ZERO), max);
193        assert_eq!(max.saturating_add(Duration::new(0, 1)), max);
194        assert_eq!(max.saturating_sub(Duration::ZERO), max);
195        assert_eq!(
196            max.saturating_sub(Duration::new(0, 1)),
197            max - Duration::new(0, 1)
198        );
199
200        let min = <T as SaturatingTime>::min_value();
201        assert_eq!(min.saturating_sub(Duration::ZERO), min);
202        assert_eq!(min.saturating_sub(Duration::new(0, 1)), min);
203        assert_eq!(min.saturating_add(Duration::ZERO), min);
204        assert_eq!(
205            min.saturating_add(Duration::new(0, 1)),
206            min + Duration::new(0, 1)
207        );
208    }
209
210    /// Verifies whether the saturating logic behind [`Duration`] types work.
211    fn saturating_duration<T: SaturatingTime + PartialEq + Debug>() {
212        // The duration from the same anchor should always be zero.
213        let anchor = T::anchor();
214        assert_eq!(anchor.saturating_duration_since(anchor), Duration::ZERO);
215
216        // Try with a later anchor.
217        let later_anchor = anchor.checked_add(Duration::from_secs(1)).unwrap();
218        assert!(later_anchor.saturating_duration_since(anchor) == Duration::from_secs(1));
219        assert_eq!(
220            anchor.saturating_duration_since(later_anchor),
221            Duration::ZERO
222        );
223
224        // Try with min and max.
225        let max = <T as SaturatingTime>::max_value();
226        let min = <T as SaturatingTime>::min_value();
227
228        // This first assertion might not be so portable, maybe remove it if
229        // this becomes a problem.
230        assert_eq!(max.saturating_duration_since(min), Duration::MAX);
231        assert_eq!(min.saturating_duration_since(max), Duration::ZERO);
232    }
233
234    /// Calls [`min_max()`] using [`SystemTime`].
235    #[test]
236    fn system_time_min_max() {
237        min_max::<SystemTime>();
238    }
239
240    /// Calls [`min_max()`] using [`Instant`].
241    #[test]
242    fn instant_min_max() {
243        min_max::<Instant>();
244    }
245
246    /// Calls [`saturating_add_sub()`] and [`saturating_duration()`] using [`SystemTime`].
247    #[test]
248    fn system_time_saturating() {
249        saturating_add_sub::<SystemTime>();
250        saturating_duration::<SystemTime>();
251    }
252
253    /// Calls [`saturating_add_sub()`] and [`saturating_duration()`] using [`Instant`].
254    #[test]
255    fn instant_saturating() {
256        saturating_add_sub::<Instant>();
257        saturating_duration::<Instant>();
258    }
259}