1use std::sync::LazyLock;
2use std::time::{Duration, SystemTime, UNIX_EPOCH};
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
6pub struct MonotonicInstant(u128);
7
8impl MonotonicInstant {
9 pub const fn from_nanos(nanos: u128) -> Self {
10 MonotonicInstant(nanos)
11 }
12
13 pub fn now() -> Self {
14 static EPOCH: LazyLock<std::time::Instant> = LazyLock::new(std::time::Instant::now);
15 let elapsed = EPOCH.elapsed();
16 MonotonicInstant(elapsed.as_nanos())
17 }
18
19 pub fn duration_since(&self, earlier: MonotonicInstant) -> Duration {
20 Duration::from_nanos(self.0.saturating_sub(earlier.0) as u64)
21 }
22
23 pub fn checked_add(&self, duration: Duration) -> Option<MonotonicInstant> {
24 self.0
25 .checked_add(duration.as_nanos())
26 .map(MonotonicInstant)
27 }
28
29 pub fn checked_sub(&self, duration: Duration) -> Option<MonotonicInstant> {
30 self.0
31 .checked_sub(duration.as_nanos())
32 .map(MonotonicInstant)
33 }
34}
35
36impl std::ops::Add<Duration> for MonotonicInstant {
37 type Output = MonotonicInstant;
38
39 fn add(self, rhs: Duration) -> Self::Output {
40 MonotonicInstant(self.0 + rhs.as_nanos())
41 }
42}
43
44impl std::ops::Sub<Duration> for MonotonicInstant {
45 type Output = MonotonicInstant;
46
47 fn sub(self, rhs: Duration) -> Self::Output {
48 MonotonicInstant(self.0 - rhs.as_nanos())
49 }
50}
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
54pub struct WallClockInstant {
55 pub secs: i64,
56 pub micros: u32,
57}
58
59const MICROS_PER_SEC: u32 = 1_000_000;
60
61impl WallClockInstant {
62 pub fn now() -> Self {
63 let duration = SystemTime::now()
64 .duration_since(UNIX_EPOCH)
65 .expect("system time before Unix epoch");
66 WallClockInstant {
67 secs: duration.as_secs() as i64,
68 micros: duration.subsec_micros(),
69 }
70 }
71
72 pub fn to_system_time(self) -> SystemTime {
73 if self.secs >= 0 {
74 UNIX_EPOCH + Duration::new(self.secs as u64, self.micros * 1000)
75 } else {
76 let positive_secs = (-self.secs) as u64;
77 if self.micros > 0 {
78 let nanos_to_subtract = (1_000_000 - self.micros) * 1000;
79 UNIX_EPOCH - Duration::new(positive_secs - 1, nanos_to_subtract)
80 } else {
81 UNIX_EPOCH - Duration::new(positive_secs, 0)
82 }
83 }
84 }
85
86 pub fn checked_add_duration(&self, other: &Duration) -> Option<WallClockInstant> {
87 let mut secs = self.secs.checked_add_unsigned(other.as_secs())?;
88 let mut micros = other.subsec_micros() + self.micros;
89 if micros >= MICROS_PER_SEC {
90 micros -= MICROS_PER_SEC;
91 secs = secs.checked_add(1)?;
92 }
93 Some(Self { secs, micros })
94 }
95
96 pub fn checked_sub_duration(&self, other: &Duration) -> Option<WallClockInstant> {
97 let mut secs = self.secs.checked_sub_unsigned(other.as_secs())?;
98 let mut micros = self.micros as i32 - other.subsec_micros() as i32;
99 if micros < 0 {
100 micros += MICROS_PER_SEC as i32;
101 secs = secs.checked_sub(1)?;
102 }
103 Some(Self {
104 secs,
105 micros: micros as u32,
106 })
107 }
108}
109
110impl std::ops::Add<Duration> for WallClockInstant {
111 type Output = WallClockInstant;
112
113 fn add(self, rhs: Duration) -> Self::Output {
114 self.checked_add_duration(&rhs)
115 .expect("duration addition overflow")
116 }
117}
118
119impl std::ops::Sub<Duration> for WallClockInstant {
120 type Output = WallClockInstant;
121
122 fn sub(self, rhs: Duration) -> Self::Output {
123 self.checked_sub_duration(&rhs)
124 .expect("duration subtraction underflow")
125 }
126}
127
128impl<T: chrono::TimeZone> From<chrono::DateTime<T>> for WallClockInstant {
129 fn from(value: chrono::DateTime<T>) -> Self {
130 WallClockInstant {
131 secs: value.timestamp(),
132 micros: value.timestamp_subsec_micros(),
133 }
134 }
135}
136
137pub trait Clock {
138 fn current_time_monotonic(&self) -> MonotonicInstant;
141
142 fn current_time_wall_clock(&self) -> WallClockInstant;
145}
146
147pub struct DefaultClock;
148
149impl Clock for DefaultClock {
150 fn current_time_monotonic(&self) -> MonotonicInstant {
151 MonotonicInstant::now()
152 }
153
154 fn current_time_wall_clock(&self) -> WallClockInstant {
155 WallClockInstant::now()
156 }
157}