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}