1use std::sync::Arc;
2use std::sync::atomic::{AtomicU64, Ordering};
3use std::time::Instant;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
9pub struct Timestamp(pub(crate) u64);
10
11impl Timestamp {
12 pub const ZERO: Timestamp = Timestamp(0);
14
15 pub fn as_nanos(&self) -> u64 {
17 self.0
18 }
19
20 pub fn as_millis(&self) -> u64 {
22 self.0 / 1_000_000
23 }
24
25 pub fn as_secs_f64(&self) -> f64 {
27 self.0 as f64 / 1_000_000_000.0
28 }
29
30 pub fn duration_since(&self, other: Timestamp) -> u64 {
33 self.0.saturating_sub(other.0)
34 }
35
36 pub fn add_nanos(&self, nanos: u64) -> Timestamp {
38 Timestamp(self.0.saturating_add(nanos))
39 }
40
41 pub fn add_millis(&self, millis: u64) -> Timestamp {
43 Timestamp(self.0.saturating_add(millis * 1_000_000))
44 }
45
46 pub fn add_secs(&self, secs: u64) -> Timestamp {
48 Timestamp(self.0.saturating_add(secs * 1_000_000_000))
49 }
50}
51
52pub trait Clock: Send + Sync {
56 fn now(&self) -> Timestamp;
58}
59
60pub struct RealClock {
62 epoch: Instant,
63}
64
65impl RealClock {
66 pub fn new() -> Self {
67 Self {
68 epoch: Instant::now(),
69 }
70 }
71}
72
73impl Default for RealClock {
74 fn default() -> Self {
75 Self::new()
76 }
77}
78
79impl Clock for RealClock {
80 fn now(&self) -> Timestamp {
81 let elapsed = self.epoch.elapsed();
82 Timestamp(elapsed.as_nanos() as u64)
83 }
84}
85
86pub struct MockClock {
103 nanos: AtomicU64,
104}
105
106impl MockClock {
107 pub fn new() -> Self {
108 Self {
109 nanos: AtomicU64::new(0),
110 }
111 }
112
113 pub fn at(timestamp: Timestamp) -> Self {
115 Self {
116 nanos: AtomicU64::new(timestamp.0),
117 }
118 }
119
120 pub fn advance_nanos(&self, nanos: u64) {
122 self.nanos.fetch_add(nanos, Ordering::Release);
123 }
124
125 pub fn advance_ms(&self, ms: u64) {
127 self.advance_nanos(ms * 1_000_000);
128 }
129
130 pub fn advance_secs(&self, secs: u64) {
132 self.advance_nanos(secs * 1_000_000_000);
133 }
134
135 pub fn set(&self, timestamp: Timestamp) {
137 self.nanos.store(timestamp.0, Ordering::Release);
138 }
139}
140
141impl Default for MockClock {
142 fn default() -> Self {
143 Self::new()
144 }
145}
146
147impl Clock for MockClock {
148 fn now(&self) -> Timestamp {
149 Timestamp(self.nanos.load(Ordering::Acquire))
150 }
151}
152
153pub(crate) fn default_clock() -> Arc<dyn Clock> {
155 Arc::new(RealClock::new())
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161
162 #[test]
163 fn timestamp_arithmetic() {
164 let t = Timestamp::ZERO;
165 assert_eq!(t.as_nanos(), 0);
166 assert_eq!(t.as_millis(), 0);
167
168 let t2 = t.add_millis(1500);
169 assert_eq!(t2.as_millis(), 1500);
170 assert_eq!(t2.as_nanos(), 1_500_000_000);
171
172 let t3 = t2.add_secs(2);
173 assert_eq!(t3.as_millis(), 3500);
174
175 assert_eq!(t3.duration_since(t2), 2_000_000_000);
176 assert_eq!(t2.duration_since(t3), 0); }
178
179 #[test]
180 fn mock_clock_advances() {
181 let clock = MockClock::new();
182 assert_eq!(clock.now(), Timestamp::ZERO);
183
184 clock.advance_ms(100);
185 assert_eq!(clock.now().as_millis(), 100);
186
187 clock.advance_secs(1);
188 assert_eq!(clock.now().as_millis(), 1100);
189 }
190
191 #[test]
192 fn mock_clock_set() {
193 let clock = MockClock::new();
194 clock.set(Timestamp(5_000_000_000));
195 assert_eq!(clock.now().as_secs_f64(), 5.0);
196 }
197
198 #[test]
199 fn real_clock_monotonic() {
200 let clock = RealClock::new();
201 let t1 = clock.now();
202 let t2 = clock.now();
203 assert!(t2 >= t1);
204 }
205
206 #[test]
207 fn timestamp_ordering() {
208 let a = Timestamp(100);
209 let b = Timestamp(200);
210 assert!(a < b);
211 assert!(b > a);
212 assert_eq!(a, Timestamp(100));
213 }
214}