saturating_time/
internal.rs1use std::{
10 cmp,
11 sync::LazyLock,
12 time::{Duration, Instant, SystemTime},
13};
14
15static MAX_SYSTEM_TIME: LazyLock<SystemTime> = LazyLock::new(find_max);
17
18static MIN_SYSTEM_TIME: LazyLock<SystemTime> = LazyLock::new(find_min);
20
21static MAX_INSTANT: LazyLock<Instant> = LazyLock::new(find_max);
23
24static MIN_INSTANT: LazyLock<Instant> = LazyLock::new(find_min);
26
27pub trait SaturatingTime: Sized + Copy {
29 fn anchor() -> Self;
31
32 fn max_value() -> Self;
34
35 fn min_value() -> Self;
37
38 fn checked_add(&self, duration: Duration) -> Option<Self>;
40
41 fn checked_sub(&self, duration: Duration) -> Option<Self>;
43
44 fn checked_duration_since(&self, earlier: Self) -> Option<Duration>;
46}
47
48impl SaturatingTime for SystemTime {
49 fn anchor() -> Self {
50 Self::UNIX_EPOCH
51 }
52
53 fn max_value() -> Self {
54 *MAX_SYSTEM_TIME
55 }
56
57 fn min_value() -> Self {
58 *MIN_SYSTEM_TIME
59 }
60
61 fn checked_add(&self, duration: Duration) -> Option<Self> {
62 Self::checked_add(self, duration)
63 }
64
65 fn checked_sub(&self, duration: Duration) -> Option<Self> {
66 Self::checked_sub(self, duration)
67 }
68
69 fn checked_duration_since(&self, earlier: Self) -> Option<Duration> {
70 Self::duration_since(self, earlier).ok()
71 }
72}
73
74impl SaturatingTime for Instant {
75 fn anchor() -> Self {
76 use web_time_compat::InstantExt;
77 Self::get()
78 }
79
80 fn max_value() -> Self {
81 *MAX_INSTANT
82 }
83
84 fn min_value() -> Self {
85 *MIN_INSTANT
86 }
87
88 fn checked_add(&self, duration: Duration) -> Option<Self> {
89 Self::checked_add(self, duration)
90 }
91
92 fn checked_sub(&self, duration: Duration) -> Option<Self> {
93 Self::checked_sub(self, duration)
94 }
95
96 fn checked_duration_since(&self, _earlier: Self) -> Option<Duration> {
101 unreachable!()
102 }
103}
104
105fn find_max<T: SaturatingTime>() -> T {
107 find_limit(T::checked_add)
108}
109
110fn find_min<T: SaturatingTime>() -> T {
112 find_limit(T::checked_sub)
113}
114
115fn find_limit<T, F>(f: F) -> T
130where
131 T: SaturatingTime,
132 F: Fn(&T, Duration) -> Option<T>,
133{
134 const INITIAL_STEP: Duration = Duration::new(1_000_000_000_000_000_000, 0);
135 const ONE_NS: Duration = Duration::new(0, 1);
136
137 let mut step = INITIAL_STEP;
139 let mut res = T::anchor();
140
141 loop {
142 let next = f(&res, step);
144 match next {
145 Some(st) => {
146 res = st;
148 }
149 None => {
150 if step == ONE_NS {
151 return res;
153 } else {
154 step = cmp::max(ONE_NS, step / 2);
156 }
157 }
158 }
159 }
160}
161
162#[cfg(test)]
163mod tests {
164 #![allow(clippy::bool_assert_comparison)]
166 #![allow(clippy::clone_on_copy)]
167 #![allow(clippy::dbg_macro)]
168 #![allow(clippy::mixed_attributes_style)]
169 #![allow(clippy::print_stderr)]
170 #![allow(clippy::print_stdout)]
171 #![allow(clippy::single_char_pattern)]
172 #![allow(clippy::unwrap_used)]
173 #![allow(clippy::unchecked_time_subtraction)]
174 #![allow(clippy::useless_vec)]
175 #![allow(clippy::needless_pass_by_value)]
176 #![allow(clippy::string_slice)] use std::{
180 fmt::Debug,
181 ops::{Add, Sub},
182 };
183
184 use super::*;
185
186 fn min_max<T>()
188 where
189 T: SaturatingTime
190 + PartialEq
191 + Debug
192 + Add<Duration, Output = T>
193 + Sub<Duration, Output = T>,
194 {
195 assert_eq!(
196 T::max_value().checked_add(Duration::ZERO),
197 Some(T::max_value())
198 );
199 assert_eq!(T::max_value().checked_add(Duration::new(0, 1)), None);
200 assert_eq!(
201 T::max_value().checked_sub(Duration::ZERO),
202 Some(T::max_value())
203 );
204 assert_eq!(
205 T::max_value().checked_sub(Duration::new(0, 1)),
206 Some(T::max_value() - Duration::new(0, 1))
207 );
208
209 assert_eq!(
210 T::min_value().checked_sub(Duration::ZERO),
211 Some(T::min_value())
212 );
213 assert_eq!(T::min_value().checked_sub(Duration::new(0, 1)), None);
214 assert_eq!(
215 T::min_value().checked_add(Duration::ZERO),
216 Some(T::min_value())
217 );
218 assert_eq!(
219 T::min_value().checked_add(Duration::new(0, 1)),
220 Some(T::min_value() + Duration::new(0, 1))
221 );
222 }
223
224 #[test]
227 fn system_time_min_max() {
228 min_max::<SystemTime>();
229 }
230
231 #[test]
234 fn instant_min_max() {
235 min_max::<Instant>();
236 }
237
238 #[cfg(target_family = "unix")]
241 #[test]
242 fn system_time_min_max_unix() {
243 assert_eq!(
244 SystemTime::max_value(),
245 SystemTime::UNIX_EPOCH + Duration::new(i64::MAX as u64, 999_999_999)
246 );
247 assert_eq!(
248 SystemTime::min_value(),
249 SystemTime::UNIX_EPOCH - Duration::new(i64::MAX as u64 + 1, 0)
250 );
251 }
252
253 #[test]
256 #[cfg(target_family = "unix")]
257 fn instant_min_max_unix() {
258 assert_eq!(
260 format!("{:?}", Instant::max_value()),
261 format!(
262 "Instant {{ tv_sec: {}, tv_nsec: {} }}",
263 i64::MAX,
264 999_999_999
265 )
266 );
267
268 assert_eq!(
269 format!("{:?}", Instant::min_value()),
270 format!("Instant {{ tv_sec: {}, tv_nsec: {} }}", i64::MIN, 0)
271 );
272 }
273}