Skip to main content

vcon_server/
clock.rs

1//! Time, behind an interface so tests can set it.
2
3use time::OffsetDateTime;
4
5/// Where the server gets the time.
6pub trait Clock: Send + Sync {
7    /// The time now, in UTC.
8    fn now(&self) -> OffsetDateTime;
9}
10
11/// A time as the catalogs keep it: nanoseconds since the Unix epoch.
12pub(crate) fn nanos(at: OffsetDateTime) -> i64 {
13    i64::try_from(at.unix_timestamp_nanos()).expect("a time before 2262")
14}
15
16/// A time the catalogs kept as `nanos` wrote it, or None if it is out of
17/// range.
18pub(crate) fn from_nanos(at: i64) -> Option<OffsetDateTime> {
19    OffsetDateTime::from_unix_timestamp_nanos(i128::from(at)).ok()
20}
21
22/// The system's clock.
23#[derive(Debug, Clone, Copy, Default)]
24pub struct SystemClock;
25
26impl Clock for SystemClock {
27    fn now(&self) -> OffsetDateTime {
28        OffsetDateTime::now_utc()
29    }
30}
31
32#[cfg(test)]
33pub(crate) use manual::ManualClock;
34
35#[cfg(test)]
36mod manual {
37    use std::sync::Mutex;
38
39    use time::OffsetDateTime;
40
41    use super::Clock;
42
43    /// A clock that stands still until a test moves it.
44    pub(crate) struct ManualClock(Mutex<OffsetDateTime>);
45
46    impl ManualClock {
47        pub(crate) fn at(now: OffsetDateTime) -> ManualClock {
48            ManualClock(Mutex::new(now))
49        }
50
51        pub(crate) fn set(&self, now: OffsetDateTime) {
52            *self.0.lock().expect("clock") = now;
53        }
54    }
55
56    impl Clock for ManualClock {
57        fn now(&self) -> OffsetDateTime {
58            *self.0.lock().expect("clock")
59        }
60    }
61}
62
63#[cfg(test)]
64mod tests {
65    use super::*;
66
67    #[test]
68    fn the_system_clock_is_close_to_now() {
69        let before = OffsetDateTime::now_utc();
70        let now = SystemClock.now();
71        let after = OffsetDateTime::now_utc();
72        assert!(
73            before <= now && now <= after,
74            "the system clock reads the time now: {before} <= {now} <= {after}"
75        );
76        assert!(
77            now.offset().is_utc(),
78            "the trait promises UTC, and comparing instants alone cannot tell"
79        );
80    }
81}