1use std::time::{Duration, SystemTime, UNIX_EPOCH};
8
9pub trait Clock {
13 fn unix_now(&self) -> u64;
15
16 fn unix_now_plus(&self, offset: Duration) -> u64 {
18 self.unix_now().saturating_add(offset.as_secs())
19 }
20}
21
22#[derive(Debug, Default, Clone, Copy)]
24pub struct SystemClock;
25
26impl SystemClock {
27 #[must_use]
29 pub fn new() -> Self {
30 Self
31 }
32}
33
34impl Clock for SystemClock {
35 fn unix_now(&self) -> u64 {
36 SystemTime::now()
37 .duration_since(UNIX_EPOCH)
38 .map(|d| d.as_secs())
39 .unwrap_or(0)
40 }
41}
42
43#[cfg(any(test, feature = "test-utils"))]
46#[derive(Debug, Clone, Copy)]
47pub struct FixedClock(pub u64);
48
49#[cfg(any(test, feature = "test-utils"))]
50impl FixedClock {
51 #[must_use]
53 pub fn at(unix_secs: u64) -> Self {
54 Self(unix_secs)
55 }
56
57 #[must_use]
59 pub fn advance(self, secs: u64) -> Self {
60 Self(self.0.saturating_add(secs))
61 }
62}
63
64#[cfg(any(test, feature = "test-utils"))]
65impl Clock for FixedClock {
66 fn unix_now(&self) -> u64 {
67 self.0
68 }
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74
75 #[test]
76 fn system_clock_is_positive() {
77 let t = SystemClock::new().unix_now();
78 assert!(t > 1_704_067_200, "clock looks wrong: {t}");
80 }
81
82 #[test]
83 fn fixed_clock_is_deterministic() {
84 let c = FixedClock::at(1_000_000);
85 assert_eq!(c.unix_now(), 1_000_000);
86 assert_eq!(c.unix_now(), 1_000_000);
87 }
88
89 #[test]
90 fn unix_now_plus_offsets_correctly() {
91 let c = FixedClock::at(1_000);
92 assert_eq!(c.unix_now_plus(Duration::from_secs(100)), 1_100);
93 }
94
95 #[test]
96 fn advance_produces_later_clock() {
97 let c = FixedClock::at(1_000).advance(60);
98 assert_eq!(c.unix_now(), 1_060);
99 }
100}