1use chrono::{DateTime, Utc};
14use std::sync::Arc;
15
16pub trait Clock: Send + Sync {
19 fn now(&self) -> DateTime<Utc>;
20}
21
22pub type ClockRef = Arc<dyn Clock>;
25
26pub struct SystemClock;
28
29impl Clock for SystemClock {
30 fn now(&self) -> DateTime<Utc> {
31 Utc::now()
32 }
33}
34
35#[cfg(any(test, feature = "test-utils"))]
39pub use fakes::{FixedClock, SteppingClock};
40
41#[cfg(any(test, feature = "test-utils"))]
42mod fakes {
43 use super::*;
44 use chrono::Duration;
45 use std::sync::atomic::{AtomicU64, Ordering};
46
47 pub struct FixedClock(pub DateTime<Utc>);
49
50 impl Clock for FixedClock {
51 fn now(&self) -> DateTime<Utc> {
52 self.0
53 }
54 }
55
56 pub struct SteppingClock {
59 start: DateTime<Utc>,
60 step: Duration,
61 calls: AtomicU64,
62 }
63
64 impl SteppingClock {
65 pub fn new(start: DateTime<Utc>, step: Duration) -> Self {
66 Self {
67 start,
68 step,
69 calls: AtomicU64::new(0),
70 }
71 }
72 }
73
74 impl Clock for SteppingClock {
75 fn now(&self) -> DateTime<Utc> {
76 let n = self.calls.fetch_add(1, Ordering::SeqCst);
77 self.start + self.step * (n as i32)
78 }
79 }
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85 use chrono::Duration;
86
87 #[test]
88 fn fixed_clock_returns_same_instant() {
89 let instant = DateTime::parse_from_rfc3339("2024-01-01T00:00:00Z")
90 .unwrap()
91 .with_timezone(&Utc);
92 let clock = FixedClock(instant);
93 assert_eq!(clock.now(), instant);
94 assert_eq!(clock.now(), instant);
95 }
96
97 #[test]
98 fn stepping_clock_advances_one_step_per_call() {
99 let start = DateTime::parse_from_rfc3339("2024-01-01T00:00:00Z")
100 .unwrap()
101 .with_timezone(&Utc);
102 let clock = SteppingClock::new(start, Duration::seconds(10));
103 assert_eq!(clock.now(), start);
104 assert_eq!(clock.now(), start + Duration::seconds(10));
105 assert_eq!(clock.now(), start + Duration::seconds(20));
106 }
107
108 #[test]
109 fn clock_is_usable_behind_the_shared_handle() {
110 let instant = DateTime::parse_from_rfc3339("2024-01-01T00:00:00Z")
111 .unwrap()
112 .with_timezone(&Utc);
113 let clock: ClockRef = Arc::new(FixedClock(instant));
114 assert_eq!(clock.now(), instant);
115 }
116}