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}