Skip to main content

saturating_time/
lib.rs

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