s2n_quic_core/time/
timestamp.rs1use crate::recovery::K_GRANULARITY;
7use core::{fmt, num::NonZeroU64, time::Duration};
8
9#[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Hash)]
21pub struct Timestamp(NonZeroU64);
22
23impl fmt::Debug for Timestamp {
24 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
25 write!(f, "Timestamp({self})")
26 }
27}
28
29impl fmt::Display for Timestamp {
30 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
31 let duration = self.as_duration_impl();
32 let micros = duration.subsec_micros();
33 let secs = duration.as_secs() % 60;
34 let mins = duration.as_secs() / 60 % 60;
35 let hours = duration.as_secs() / 60 / 60;
36 if micros != 0 {
37 write!(f, "{hours}:{mins:02}:{secs:02}.{micros:06}")
38 } else {
39 write!(f, "{hours}:{mins:02}:{secs:02}")
40 }
41 }
42}
43
44#[test]
45fn fmt_test() {
46 macro_rules! debug {
47 ($secs:expr, $micros:expr) => {
48 format!(
49 "{:#?}",
50 Timestamp::from_duration_impl(
51 Duration::from_secs($secs) + Duration::from_micros($micros)
52 )
53 )
54 };
55 }
56
57 assert_eq!(debug!(1, 0), "Timestamp(0:00:01)");
58 assert_eq!(debug!(1, 1), "Timestamp(0:00:01.000001)");
59 assert_eq!(debug!(123456789, 123456), "Timestamp(34293:33:09.123456)");
60}
61
62const ONE_MICROSECOND: NonZeroU64 = NonZeroU64::new(1).unwrap();
64
65impl Timestamp {
66 #[inline]
71 pub fn checked_add(self, duration: Duration) -> Option<Self> {
72 self.as_duration_impl()
73 .checked_add(duration)
74 .map(Self::from_duration_impl)
75 }
76
77 #[inline]
82 pub fn checked_sub(self, duration: Duration) -> Option<Self> {
83 self.as_duration_impl()
84 .checked_sub(duration)
85 .map(Self::from_duration_impl)
86 }
87
88 #[inline]
91 pub fn saturating_duration_since(self, earlier: Self) -> Duration {
92 self.checked_sub(earlier.as_duration_impl())
93 .map(Self::as_duration_impl)
94 .unwrap_or_default()
95 }
96
97 #[inline]
103 pub unsafe fn from_duration(duration: Duration) -> Self {
104 Self::from_duration_impl(duration)
105 }
106
107 #[inline]
109 fn from_duration_impl(duration: Duration) -> Self {
110 debug_assert!(duration.as_micros() <= u64::MAX.into());
112 let micros = duration.as_micros() as u64;
113 let micros = NonZeroU64::new(micros).unwrap_or(ONE_MICROSECOND);
115 Self(micros)
116 }
117
118 #[inline]
123 pub unsafe fn as_duration(self) -> Duration {
124 Self::as_duration_impl(self)
125 }
126
127 #[inline]
129 const fn as_duration_impl(self) -> Duration {
130 Duration::from_micros(self.0.get())
131 }
132
133 #[inline]
138 pub const fn has_elapsed(self, now: Self) -> bool {
139 let mut now = now.0.get();
140
141 now += K_GRANULARITY.as_micros() as u64;
143
144 self.0.get() < now
145 }
146}
147
148impl core::ops::Add<Duration> for Timestamp {
149 type Output = Timestamp;
150
151 #[inline]
152 fn add(self, rhs: Duration) -> Self::Output {
153 Timestamp::from_duration_impl(self.as_duration_impl() + rhs)
154 }
155}
156
157impl core::ops::AddAssign<Duration> for Timestamp {
158 #[inline]
159 fn add_assign(&mut self, other: Duration) {
160 *self = *self + other;
161 }
162}
163
164impl core::ops::Sub for Timestamp {
165 type Output = Duration;
166
167 #[inline]
168 fn sub(self, rhs: Timestamp) -> Self::Output {
169 self.as_duration_impl() - rhs.as_duration_impl()
170 }
171}
172
173impl core::ops::Sub<Duration> for Timestamp {
174 type Output = Timestamp;
175
176 #[inline]
177 fn sub(self, rhs: Duration) -> Self::Output {
178 Timestamp::from_duration_impl(self.as_duration_impl() - rhs)
179 }
180}
181
182impl core::ops::SubAssign<Duration> for Timestamp {
183 #[inline]
184 fn sub_assign(&mut self, other: Duration) {
185 *self = *self - other;
186 }
187}
188
189#[cfg(test)]
190mod tests {
191 use super::*;
192
193 #[test]
194 fn timestamp_from_and_to_duration() {
195 let ts1 = Timestamp::from_duration_impl(Duration::from_millis(100));
196 let ts2 = Timestamp::from_duration_impl(Duration::from_millis(220));
197
198 assert_eq!(Duration::from_millis(120), ts2 - ts1);
200
201 let ts3 = ts2 + Duration::from_millis(11);
203 assert_eq!(Duration::from_millis(231), unsafe {
204 Timestamp::as_duration(ts3)
205 });
206
207 let ts4 = ts3 - Duration::from_millis(41);
209 assert_eq!(Duration::from_millis(190), unsafe {
210 Timestamp::as_duration(ts4)
211 });
212 }
213
214 fn timestamp_math(initial: Timestamp) {
215 let mut ts1 = initial + Duration::from_millis(500);
217 assert_eq!(Duration::from_millis(500), ts1 - initial);
218 ts1 += Duration::from_millis(100);
220 assert_eq!(Duration::from_millis(600), ts1 - initial);
221 ts1 -= Duration::from_millis(50);
223 assert_eq!(Duration::from_millis(550), ts1 - initial);
224 let ts2 = ts1 - Duration::from_millis(110);
226 assert_eq!(Duration::from_millis(440), ts2 - initial);
227 assert!(ts2.checked_sub(Duration::from_secs(u64::MAX)).is_none());
229 assert_eq!(Some(initial), ts2.checked_sub(Duration::from_millis(440)));
230 let max_duration =
232 Duration::from_secs(u64::MAX) + (Duration::from_secs(1) - Duration::from_nanos(1));
233 assert_eq!(None, ts2.checked_add(max_duration));
234 assert!(ts2.checked_add(Duration::from_secs(24 * 60 * 60)).is_some());
235
236 let higher = initial + Duration::from_millis(200);
238 assert_eq!(
239 Duration::from_millis(200),
240 higher.saturating_duration_since(initial)
241 );
242 assert_eq!(
243 Duration::from_millis(0),
244 initial.saturating_duration_since(higher)
245 );
246 }
247
248 #[test]
249 fn timestamp_math_test() {
250 let initial = Timestamp::from_duration_impl(Duration::from_micros(1));
252 timestamp_math(initial);
253
254 let initial = Timestamp::from_duration_impl(Duration::from_micros(1u64 << 63));
256 timestamp_math(initial);
257 }
258}