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}