1use std::fmt;
11use std::ops;
12use std::time;
13
14#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
19pub struct MonotonicTs(u64);
20
21impl MonotonicTs {
22 pub const ZERO: Self = Self(0);
24
25 #[must_use]
27 pub fn from_nanos(nanos: u64) -> Self {
28 Self(nanos)
29 }
30
31 #[must_use]
33 pub fn as_nanos(self) -> u64 {
34 self.0
35 }
36
37 #[must_use]
39 pub fn as_secs_f64(self) -> f64 {
40 self.0 as f64 / 1_000_000_000.0
41 }
42
43 #[must_use]
47 pub fn checked_duration_since(self, other: Self) -> Option<Duration> {
48 self.0.checked_sub(other.0).map(Duration::from_nanos)
49 }
50
51 #[must_use]
53 pub fn saturating_duration_since(self, other: Self) -> Duration {
54 Duration::from_nanos(self.0.saturating_sub(other.0))
55 }
56}
57
58impl fmt::Display for MonotonicTs {
59 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60 write!(f, "{}ns", self.0)
61 }
62}
63
64impl fmt::Debug for MonotonicTs {
65 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66 write!(f, "MonotonicTs({}ns)", self.0)
67 }
68}
69
70impl ops::Add<Duration> for MonotonicTs {
71 type Output = Self;
72 fn add(self, rhs: Duration) -> Self {
73 Self(self.0 + rhs.as_nanos())
74 }
75}
76
77#[derive(Clone, Copy, PartialEq, Eq)]
82pub struct WallTs(i64);
83
84impl WallTs {
85 #[must_use]
87 pub fn from_micros(micros: i64) -> Self {
88 Self(micros)
89 }
90
91 #[must_use]
93 pub fn now() -> Self {
94 let d = time::SystemTime::now()
95 .duration_since(time::UNIX_EPOCH)
96 .unwrap_or_default();
97 Self(d.as_micros() as i64)
98 }
99
100 #[must_use]
102 pub fn as_micros(self) -> i64 {
103 self.0
104 }
105}
106
107impl fmt::Display for WallTs {
108 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109 write!(f, "{}μs", self.0)
110 }
111}
112
113impl fmt::Debug for WallTs {
114 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115 write!(f, "WallTs({}μs)", self.0)
116 }
117}
118
119#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
124pub struct Duration(u64);
125
126impl Duration {
127 pub const ZERO: Self = Self(0);
129
130 #[must_use]
132 pub fn from_nanos(nanos: u64) -> Self {
133 Self(nanos)
134 }
135
136 #[must_use]
138 pub fn from_micros(micros: u64) -> Self {
139 Self(micros * 1_000)
140 }
141
142 #[must_use]
144 pub fn from_millis(millis: u64) -> Self {
145 Self(millis * 1_000_000)
146 }
147
148 #[must_use]
150 pub fn from_secs(secs: u64) -> Self {
151 Self(secs * 1_000_000_000)
152 }
153
154 #[must_use]
156 pub fn as_nanos(self) -> u64 {
157 self.0
158 }
159
160 #[must_use]
162 pub fn as_secs_f64(self) -> f64 {
163 self.0 as f64 / 1_000_000_000.0
164 }
165}
166
167impl fmt::Display for Duration {
168 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
169 if self.0 >= 1_000_000_000 {
170 write!(f, "{:.3}s", self.as_secs_f64())
171 } else if self.0 >= 1_000_000 {
172 write!(f, "{:.3}ms", self.0 as f64 / 1_000_000.0)
173 } else if self.0 >= 1_000 {
174 write!(f, "{:.1}μs", self.0 as f64 / 1_000.0)
175 } else {
176 write!(f, "{}ns", self.0)
177 }
178 }
179}
180
181impl fmt::Debug for Duration {
182 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
183 write!(f, "Duration({}ns)", self.0)
184 }
185}
186
187impl From<std::time::Duration> for Duration {
188 fn from(d: std::time::Duration) -> Self {
189 Self(d.as_nanos() as u64)
190 }
191}
192
193impl From<Duration> for std::time::Duration {
194 fn from(d: Duration) -> Self {
195 std::time::Duration::from_nanos(d.0)
196 }
197}
198
199#[cfg(test)]
200mod tests {
201 use super::*;
202
203 #[test]
204 fn monotonic_ordering() {
205 let a = MonotonicTs::from_nanos(100);
206 let b = MonotonicTs::from_nanos(200);
207 assert!(a < b);
208 }
209
210 #[test]
211 fn duration_conversion() {
212 let d = Duration::from_millis(42);
213 let std_d: std::time::Duration = d.into();
214 assert_eq!(std_d.as_millis(), 42);
215 }
216
217 #[test]
218 fn checked_duration_since() {
219 let a = MonotonicTs::from_nanos(300);
220 let b = MonotonicTs::from_nanos(100);
221 let d = a.checked_duration_since(b).unwrap();
222 assert_eq!(d.as_nanos(), 200);
223 assert!(b.checked_duration_since(a).is_none());
224 }
225}