1use std::sync::LazyLock;
4use std::sync::atomic::{AtomicI64, AtomicU64, Ordering};
5use std::time::{Duration, SystemTime, UNIX_EPOCH};
6
7use tokio::time::Instant;
8
9const RESYNC_EVERY_MS: u64 = 60 * 60 * 1000;
11
12struct State {
13 start_instant: Instant,
14
15 skew_ms: AtomicI64,
19
20 next_resync_elapsed_ms: AtomicU64,
22}
23
24static STATE: LazyLock<State> = LazyLock::new(State::init);
25
26impl State {
27 fn init() -> Self {
28 let start_instant = Instant::now();
29 let unix_ms = unix_timestamp_millis_slow();
30 Self {
31 start_instant,
32 skew_ms: AtomicI64::new(unix_ms),
33 next_resync_elapsed_ms: AtomicU64::new(RESYNC_EVERY_MS),
34 }
35 }
36
37 #[inline]
38 fn elapsed_ms_now(&self) -> u64 {
39 self.start_instant.elapsed().as_millis() as u64
40 }
41
42 #[inline]
43 fn elapsed_nanos(&self) -> u64 {
44 self.start_instant.elapsed().as_nanos() as u64
45 }
46
47 #[inline]
48 fn instant_from_nanos(&self, nanos: u64) -> Instant {
49 self.start_instant + Duration::from_nanos(nanos)
50 }
51
52 fn maybe_resync(&self, elapsed_now_ms: u64) {
53 let next = self.next_resync_elapsed_ms.load(Ordering::Relaxed);
54 if elapsed_now_ms < next {
55 return;
56 }
57
58 let new_next = elapsed_now_ms.saturating_add(RESYNC_EVERY_MS);
59
60 if self
62 .next_resync_elapsed_ms
63 .compare_exchange(next, new_next, Ordering::Relaxed, Ordering::Relaxed)
64 .is_err()
65 {
66 return;
67 }
68
69 let unix_now_ms = unix_timestamp_millis_slow();
70 let new_skew = unix_now_ms - elapsed_now_ms as i64;
71 self.skew_ms.store(new_skew, Ordering::Relaxed);
72 }
73
74 fn now_unix_ms(&self) -> i64 {
75 let elapsed_now_ms = self.elapsed_ms_now();
76 self.maybe_resync(elapsed_now_ms);
77
78 let skew = self.skew_ms.load(Ordering::Relaxed);
79 elapsed_now_ms as i64 + skew
80 }
81}
82
83#[inline(always)]
84pub fn unix_timestamp_secs() -> i64 {
86 unix_timestamp_secs_from_millis(unix_timestamp_millis())
87}
88
89#[inline(always)]
90const fn unix_timestamp_secs_from_millis(ms: i64) -> i64 {
91 ms.div_euclid(1000)
92}
93
94#[inline(always)]
95pub fn unix_timestamp_millis() -> i64 {
97 unix_timestamp_millis_slow()
98}
99
100fn unix_timestamp_millis_slow() -> i64 {
101 match SystemTime::now().duration_since(UNIX_EPOCH) {
102 Ok(d) => d.as_millis().try_into().unwrap_or(i64::MAX),
103 Err(e) => {
104 let ms: i64 = e.duration().as_millis().try_into().unwrap_or(i64::MAX);
105 -ms
106 }
107 }
108}
109
110pub fn now_unix_ms() -> i64 {
115 STATE.now_unix_ms()
116}
117
118#[must_use]
123pub fn now_monotonic_nanos() -> u64 {
124 STATE.elapsed_nanos()
125}
126
127#[inline]
128pub fn now_unix() -> i64 {
133 now_unix_ms().div_euclid(1000)
136}
137
138#[inline]
139pub fn now_system_time() -> SystemTime {
144 let ms = now_unix_ms();
145 let epoch = SystemTime::UNIX_EPOCH;
146 let elapsed_since_epoch = Duration::from_millis(ms.wrapping_abs() as u64);
147 if ms >= 0 {
148 epoch + elapsed_since_epoch
149 } else {
150 epoch - elapsed_since_epoch
151 }
152}
153
154pub fn time_modulo_index(len: usize) -> usize {
156 if len == 0 {
157 return 0;
158 }
159
160 (now_unix_ms() as u64 % len as u64) as usize
162}
163
164#[derive(Debug)]
171pub struct AtomicInstant(AtomicU64);
172
173impl AtomicInstant {
174 #[must_use]
176 pub fn now() -> Self {
177 Self(AtomicU64::new(now_monotonic_nanos()))
178 }
179
180 pub fn set_now(&self) {
182 self.0.store(now_monotonic_nanos(), Ordering::Relaxed);
183 }
184
185 #[must_use]
187 pub fn as_nanos(&self) -> u64 {
188 self.0.load(Ordering::Relaxed)
189 }
190
191 #[must_use]
193 pub fn elapsed(&self) -> Duration {
194 Duration::from_nanos(now_monotonic_nanos().saturating_sub(self.as_nanos()))
195 }
196
197 #[must_use]
199 pub fn to_instant(&self) -> Instant {
200 STATE.instant_from_nanos(self.as_nanos())
201 }
202}
203
204impl Default for AtomicInstant {
205 fn default() -> Self {
206 Self::now()
207 }
208}
209
210impl From<&AtomicInstant> for Instant {
211 fn from(value: &AtomicInstant) -> Self {
212 value.to_instant()
213 }
214}
215
216#[cfg(test)]
217mod tests {
218 use super::*;
219 use std::{thread, time::Duration};
220
221 #[test]
222 fn test_progression_non_decreasing() {
223 let a = now_unix_ms();
224 thread::sleep(Duration::from_millis(2));
225 let b = now_unix_ms();
226 assert!(b >= a);
227 }
228
229 #[test]
230 fn now_unix_ms_is_close_to_system_clock() {
231 let sys = unix_timestamp_millis();
232 let approx = now_unix_ms();
233 let diff = (sys - approx).abs();
234 assert!(diff <= 1_000);
235 }
236
237 #[test]
238 fn unix_timestamp_secs_matches_system_clock() {
239 let before = unix_timestamp_millis().div_euclid(1000);
240 let actual = unix_timestamp_secs();
241 let after = unix_timestamp_millis().div_euclid(1000);
242
243 assert!((before..=after).contains(&actual));
244 }
245
246 #[test]
247 fn unix_timestamp_secs_floors_milliseconds() {
248 for (millis, seconds) in [
249 (-1_001, -2),
250 (-1_000, -1),
251 (-999, -1),
252 (-1, -1),
253 (0, 0),
254 (1, 0),
255 (999, 0),
256 (1_000, 1),
257 (1_001, 1),
258 ] {
259 assert_eq!(unix_timestamp_secs_from_millis(millis), seconds);
260 }
261 }
262
263 #[test]
264 fn test_skew_math() {
265 let state = State::init();
266 let elapsed = state.elapsed_ms_now();
267
268 let target_unix = unix_timestamp_millis_slow() + 1234;
270 state
271 .skew_ms
272 .store(target_unix - elapsed as i64, Ordering::Relaxed);
273
274 let now = state.now_unix_ms();
275 assert!(now >= target_unix);
276 }
277
278 #[test]
279 fn modulo_index_zero_len() {
280 assert_eq!(time_modulo_index(0), 0);
281 }
282
283 #[test]
284 fn atomic_instant_roundtrip_and_elapsed() {
285 let t = AtomicInstant::now();
286 thread::sleep(Duration::from_millis(5));
287 assert!(t.elapsed() >= Duration::from_millis(5));
288
289 let later = AtomicInstant::now();
291 assert!(later.as_nanos() > t.as_nanos());
292
293 let recovered = t.to_instant();
295 assert!(recovered.elapsed() >= Duration::from_millis(5));
296
297 let before = t.as_nanos();
299 thread::sleep(Duration::from_millis(1));
300 t.set_now();
301 assert!(t.as_nanos() > before);
302 }
303
304 #[test]
305 fn now_unix_floor_division() {
306 let sys_s = unix_timestamp_millis() / 1000;
309 let approx_s = now_unix();
310 let ms = now_unix_ms();
311 assert_eq!(approx_s, ms.div_euclid(1000));
312 assert!((sys_s - approx_s).abs() <= 1);
314 }
315
316 #[test]
317 fn test_modulo_bounds() {
318 for len in 1..50 {
319 assert!(time_modulo_index(len) < len);
320 }
321 }
322}