1use chrono::{DateTime, Utc};
2
3pub trait SystemClock: Send + Sync {
4 /// Returns the current date and time in UTC.
5 fn utc_now(&self) -> DateTime<Utc>;
6}
7
8pub struct RealSystemClock;
9
10impl SystemClock for RealSystemClock {
11 fn utc_now(&self) -> DateTime<Utc> {
12 Utc::now()
13 }
14}
15
16#[cfg(any(test, feature = "test-support"))]
17pub struct FakeSystemClockState {
18 now: DateTime<Utc>,
19}
20
21#[cfg(any(test, feature = "test-support"))]
22pub struct FakeSystemClock {
23 // Use an unfair lock to ensure tests are deterministic.
24 state: parking_lot::Mutex<FakeSystemClockState>,
25}
26
27#[cfg(any(test, feature = "test-support"))]
28impl Default for FakeSystemClock {
29 fn default() -> Self {
30 Self::new(Utc::now())
31 }
32}
33
34#[cfg(any(test, feature = "test-support"))]
35impl FakeSystemClock {
36 pub fn new(now: DateTime<Utc>) -> Self {
37 let state = FakeSystemClockState { now };
38
39 Self {
40 state: parking_lot::Mutex::new(state),
41 }
42 }
43
44 pub fn set_now(&self, now: DateTime<Utc>) {
45 self.state.lock().now = now;
46 }
47
48 /// Advances the [`FakeSystemClock`] by the specified [`Duration`](chrono::Duration).
49 pub fn advance(&self, duration: chrono::Duration) {
50 self.state.lock().now += duration;
51 }
52}
53
54#[cfg(any(test, feature = "test-support"))]
55impl SystemClock for FakeSystemClock {
56 fn utc_now(&self) -> DateTime<Utc> {
57 self.state.lock().now
58 }
59}