1use std::sync::{Arc, atomic::AtomicI64};
14
15use chrono::{DateTime, TimeZone, Utc};
16
17pub trait Clock: Sync {
19 fn now(&self) -> DateTime<Utc>;
21}
22
23impl<C: Clock + Send + ?Sized> Clock for Arc<C> {
24 fn now(&self) -> DateTime<Utc> {
25 (**self).now()
26 }
27}
28
29impl<C: Clock + ?Sized> Clock for Box<C> {
30 fn now(&self) -> DateTime<Utc> {
31 (**self).now()
32 }
33}
34
35#[derive(Clone, Default)]
37pub struct SystemClock {
38 _private: (),
39}
40
41impl Clock for SystemClock {
42 fn now(&self) -> DateTime<Utc> {
43 #[allow(clippy::disallowed_methods)]
45 Utc::now()
46 }
47}
48
49pub struct MockClock {
52 timestamp: AtomicI64,
53}
54
55impl Default for MockClock {
56 fn default() -> Self {
57 let datetime = Utc.with_ymd_and_hms(2022, 1, 16, 14, 40, 0).unwrap();
58 Self::new(datetime)
59 }
60}
61
62impl MockClock {
63 #[must_use]
65 pub fn new(datetime: DateTime<Utc>) -> Self {
66 let timestamp = AtomicI64::new(datetime.timestamp());
67 Self { timestamp }
68 }
69
70 pub fn advance(&self, duration: chrono::Duration) {
72 self.timestamp
73 .fetch_add(duration.num_seconds(), std::sync::atomic::Ordering::Relaxed);
74 }
75}
76
77impl Clock for MockClock {
78 fn now(&self) -> DateTime<Utc> {
79 let timestamp = self.timestamp.load(std::sync::atomic::Ordering::Relaxed);
80 chrono::TimeZone::timestamp_opt(&Utc, timestamp, 0).unwrap()
81 }
82}
83
84#[cfg(test)]
85mod tests {
86 use chrono::Duration;
87
88 use super::*;
89
90 #[test]
91 fn test_mocked_clock() {
92 let clock = MockClock::default();
93
94 let first = clock.now();
96 std::thread::sleep(std::time::Duration::from_millis(10));
97 let second = clock.now();
98
99 assert_eq!(first, second);
100
101 clock.advance(Duration::microseconds(10 * 1000 * 1000));
103 let third = clock.now();
104 assert_eq!(first + Duration::microseconds(10 * 1000 * 1000), third);
105 }
106
107 #[test]
108 fn test_real_clock() {
109 let clock = SystemClock::default();
110
111 let first = clock.now();
113 std::thread::sleep(std::time::Duration::from_millis(10));
114 let second = clock.now();
115
116 assert_ne!(first, second);
117 assert!(first < second);
118 }
119}