1use std::time::Instant;
8
9use made_core::ports::ClockPort;
10use made_core::value_objects::DurationMs;
11use time::OffsetDateTime;
12
13#[derive(Debug, Clone, Copy)]
16pub struct SystemClock {
17 started_at: Instant,
18}
19
20impl SystemClock {
21 #[must_use]
22 pub fn new() -> Self {
23 Self {
24 started_at: Instant::now(),
25 }
26 }
27}
28
29impl Default for SystemClock {
30 fn default() -> Self {
31 Self::new()
32 }
33}
34
35impl ClockPort for SystemClock {
36 fn now(&self) -> OffsetDateTime {
37 OffsetDateTime::now_utc()
38 }
39
40 fn uptime(&self) -> DurationMs {
41 DurationMs::from_millis(
42 u64::try_from(self.started_at.elapsed().as_millis()).unwrap_or(u64::MAX),
43 )
44 }
45}
46
47#[cfg(test)]
48mod tests {
49 use super::*;
50 use std::thread::sleep;
51 use std::time::Duration;
52
53 #[test]
54 fn now_returns_utc() {
55 let now = SystemClock::new().now();
56 assert_eq!(now.offset(), time::UtcOffset::UTC);
57 }
58
59 #[test]
60 fn subsequent_reads_are_monotonically_non_decreasing() {
61 let clock = SystemClock::new();
62 let a = clock.now();
63 sleep(Duration::from_millis(1));
64 let b = clock.now();
65 assert!(b >= a, "wall clock went backwards: {a:?} -> {b:?}");
66 }
67}