1use std::fmt::Debug;
18use std::time::{SystemTime, UNIX_EPOCH};
19
20#[cfg(any(test, feature = "testing"))]
21use std::sync::Mutex;
22
23pub trait Clock: Send + Sync + Debug {
29 fn now_millis(&self) -> u64;
31
32 fn now_rfc3339(&self) -> String;
34
35 fn now_secs(&self) -> i64 {
39 (self.now_millis() / 1000) as i64
40 }
41}
42
43#[derive(Debug, Clone, Copy, Default)]
48pub struct SystemClock;
49
50impl Clock for SystemClock {
51 fn now_millis(&self) -> u64 {
52 SystemTime::now()
53 .duration_since(UNIX_EPOCH)
54 .map(|d| d.as_millis() as u64)
55 .unwrap_or(0)
56 }
57
58 fn now_rfc3339(&self) -> String {
59 chrono::Utc::now().to_rfc3339()
60 }
61}
62
63#[cfg(any(test, feature = "testing"))]
91pub struct FixedClock {
92 state: Mutex<FixedClockState>,
93}
94
95#[cfg(any(test, feature = "testing"))]
96struct FixedClockState {
97 millis: u64,
98 held: bool,
99}
100
101#[cfg(any(test, feature = "testing"))]
105pub struct ClockHold<'a>(&'a FixedClock);
106
107#[cfg(any(test, feature = "testing"))]
108impl Drop for ClockHold<'_> {
109 fn drop(&mut self) {
110 self.0.state.lock().unwrap().held = false;
111 }
112}
113
114#[cfg(any(test, feature = "testing"))]
115impl FixedClock {
116 pub fn new(millis: u64) -> Self {
118 Self {
119 state: Mutex::new(FixedClockState {
120 millis,
121 held: false,
122 }),
123 }
124 }
125
126 pub fn hold(&self) -> ClockHold<'_> {
140 self.state.lock().unwrap().held = true;
141 ClockHold(self)
142 }
143
144 pub fn advance(&self, ms: u64) {
146 self.state.lock().unwrap().millis += ms;
147 }
148
149 pub fn set(&self, ms: u64) {
151 self.state.lock().unwrap().millis = ms;
152 }
153
154 pub fn get(&self) -> u64 {
156 self.state.lock().unwrap().millis
157 }
158}
159
160#[cfg(any(test, feature = "testing"))]
161impl Clock for FixedClock {
162 fn now_millis(&self) -> u64 {
163 let mut state = self.state.lock().unwrap();
164 if state.held {
165 state.millis
166 } else {
167 let t = state.millis;
168 state.millis += 1;
169 t
170 }
171 }
172
173 fn now_rfc3339(&self) -> String {
174 use chrono::{TimeZone, Utc};
175 let millis = self.now_millis();
177 let secs = (millis / 1000) as i64;
178 let nanos = ((millis % 1000) * 1_000_000) as u32;
179 Utc.timestamp_opt(secs, nanos)
180 .single()
181 .map(|dt| dt.to_rfc3339())
182 .unwrap_or_else(|| "1970-01-01T00:00:00+00:00".to_string())
183 }
184}
185
186#[cfg(any(test, feature = "testing"))]
187impl Default for FixedClock {
188 fn default() -> Self {
189 Self::new(1704067200000)
191 }
192}
193
194#[cfg(any(test, feature = "testing"))]
195impl Clone for FixedClock {
196 fn clone(&self) -> Self {
197 Self::new(self.get())
199 }
200}
201
202#[cfg(any(test, feature = "testing"))]
203impl Debug for FixedClock {
204 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
205 let state = self.state.lock().unwrap();
206 f.debug_struct("FixedClock")
207 .field("millis", &state.millis)
208 .field("held", &state.held)
209 .finish()
210 }
211}
212
213#[cfg(test)]
214mod fixed_clock_tests {
215 use super::*;
216
217 #[test]
218 fn fixed_clock_auto_advances() {
219 let clock = FixedClock::new(1000);
220 let t1 = clock.now_millis();
221 assert_eq!(t1, 1000); let t2 = clock.now_millis();
223 let t3 = clock.now_millis();
224 assert!(t2 > t1); assert!(t3 > t2);
226 }
227
228 #[test]
229 fn fixed_clock_get_does_not_advance() {
230 let clock = FixedClock::new(1000);
231 let initial = clock.get();
232 assert_eq!(clock.get(), initial); assert_eq!(clock.get(), initial);
234 let after_now = clock.now_millis(); assert!(clock.get() > initial); assert_eq!(after_now, initial); }
238
239 #[test]
240 fn fixed_clock_hold_freezes() {
241 let clock = FixedClock::new(1000);
242 let frozen_value = {
243 let _hold = clock.hold();
244 let v1 = clock.now_millis();
245 let v2 = clock.now_millis();
246 let v3 = clock.now_millis();
247 assert_eq!(v1, v2); assert_eq!(v2, v3);
249 v1
250 };
251 let t1 = clock.now_millis();
253 let t2 = clock.now_millis();
254 assert_eq!(t1, frozen_value); assert!(t2 > t1); }
257
258 #[test]
259 fn fixed_clock_manual_advance() {
260 let clock = FixedClock::new(1000);
261 clock.advance(500);
262 assert_eq!(clock.get(), 1500);
263 }
264
265 #[test]
266 fn fixed_clock_set() {
267 let clock = FixedClock::new(1000);
268 clock.set(5000);
269 assert_eq!(clock.get(), 5000);
270 }
271
272 #[test]
273 fn fixed_clock_rfc3339() {
274 let clock = FixedClock::new(1704067200000);
276 let _hold = clock.hold();
277 let rfc3339 = clock.now_rfc3339();
278 assert!(rfc3339.starts_with("2024-01-01T00:00:00"));
279 }
280}