1use core::{fmt, ops};
14
15#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
25pub struct Instant {
26 micros: i64,
27}
28
29impl Instant {
30 pub const ZERO: Instant = Instant::from_micros_const(0);
31
32 pub fn from_micros<T: Into<i64>>(micros: T) -> Instant {
34 Instant {
35 micros: micros.into(),
36 }
37 }
38
39 pub const fn from_micros_const(micros: i64) -> Instant {
40 Instant { micros }
41 }
42
43 pub fn from_millis<T: Into<i64>>(millis: T) -> Instant {
45 Instant {
46 micros: millis.into() * 1000,
47 }
48 }
49
50 pub const fn from_millis_const(millis: i64) -> Instant {
52 Instant {
53 micros: millis * 1000,
54 }
55 }
56
57 pub fn from_secs<T: Into<i64>>(secs: T) -> Instant {
59 Instant {
60 micros: secs.into() * 1000000,
61 }
62 }
63
64 #[cfg(feature = "std")]
71 pub fn now() -> Instant {
72 Self::from(::std::time::SystemTime::now())
73 }
74
75 pub const fn millis(&self) -> i64 {
78 self.micros % 1000000 / 1000
79 }
80
81 pub const fn micros(&self) -> i64 {
84 self.micros % 1000000
85 }
86
87 pub const fn secs(&self) -> i64 {
90 self.micros / 1000000
91 }
92
93 pub const fn total_millis(&self) -> i64 {
96 self.micros / 1000
97 }
98 pub const fn total_micros(&self) -> i64 {
101 self.micros
102 }
103}
104
105#[cfg(feature = "std")]
106impl From<::std::time::Instant> for Instant {
107 fn from(other: ::std::time::Instant) -> Instant {
108 static REFERENTIAL: ::std::sync::LazyLock<::std::time::Instant> =
109 ::std::sync::LazyLock::new(::std::time::Instant::now);
110
111 let n = other.saturating_duration_since(*REFERENTIAL);
112 Self::from_micros(n.as_secs() as i64 * 1000000 + n.subsec_micros() as i64)
113 }
114}
115
116#[cfg(feature = "std")]
117impl From<::std::time::SystemTime> for Instant {
118 fn from(other: ::std::time::SystemTime) -> Instant {
119 let n = other
120 .duration_since(::std::time::UNIX_EPOCH)
121 .expect("start time must not be before the unix epoch");
122 Self::from_micros(n.as_secs() as i64 * 1000000 + n.subsec_micros() as i64)
123 }
124}
125
126#[cfg(feature = "std")]
127impl From<Instant> for ::std::time::SystemTime {
128 fn from(val: Instant) -> Self {
129 ::std::time::UNIX_EPOCH + ::std::time::Duration::from_micros(val.micros as u64)
130 }
131}
132
133impl fmt::Display for Instant {
134 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
135 write!(f, "{}.{:0>3}s", self.secs(), self.millis())
136 }
137}
138
139#[cfg(feature = "defmt")]
140impl defmt::Format for Instant {
141 fn format(&self, f: defmt::Formatter) {
142 defmt::write!(f, "{}.{:03}s", self.secs(), self.millis());
143 }
144}
145
146impl ops::Add<Duration> for Instant {
147 type Output = Instant;
148
149 fn add(self, rhs: Duration) -> Instant {
150 Instant::from_micros(self.micros + rhs.total_micros() as i64)
151 }
152}
153
154impl ops::AddAssign<Duration> for Instant {
155 fn add_assign(&mut self, rhs: Duration) {
156 self.micros += rhs.total_micros() as i64;
157 }
158}
159
160impl ops::Sub<Duration> for Instant {
161 type Output = Instant;
162
163 fn sub(self, rhs: Duration) -> Instant {
164 Instant::from_micros(self.micros - rhs.total_micros() as i64)
165 }
166}
167
168impl ops::SubAssign<Duration> for Instant {
169 fn sub_assign(&mut self, rhs: Duration) {
170 self.micros -= rhs.total_micros() as i64;
171 }
172}
173
174impl ops::Sub<Instant> for Instant {
175 type Output = Duration;
176
177 fn sub(self, rhs: Instant) -> Duration {
178 Duration::from_micros((self.micros - rhs.micros).unsigned_abs())
179 }
180}
181
182#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
184pub struct Duration {
185 micros: u64,
186}
187
188impl Duration {
189 pub const ZERO: Duration = Duration::from_micros(0);
190 pub const MAX: Duration = Duration::from_micros(u64::MAX);
192 pub const fn from_micros(micros: u64) -> Duration {
194 Duration { micros }
195 }
196
197 pub const fn from_millis(millis: u64) -> Duration {
199 Duration {
200 micros: millis * 1000,
201 }
202 }
203
204 pub const fn from_secs(secs: u64) -> Duration {
206 Duration {
207 micros: secs * 1000000,
208 }
209 }
210
211 pub const fn millis(&self) -> u64 {
213 self.micros / 1000 % 1000
214 }
215
216 pub const fn micros(&self) -> u64 {
218 self.micros % 1000000
219 }
220
221 pub const fn secs(&self) -> u64 {
223 self.micros / 1000000
224 }
225
226 pub const fn total_millis(&self) -> u64 {
228 self.micros / 1000
229 }
230
231 pub const fn total_micros(&self) -> u64 {
233 self.micros
234 }
235}
236
237impl fmt::Display for Duration {
238 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
239 write!(f, "{}.{:03}s", self.secs(), self.millis())
240 }
241}
242
243#[cfg(feature = "defmt")]
244impl defmt::Format for Duration {
245 fn format(&self, f: defmt::Formatter) {
246 defmt::write!(f, "{}.{:03}s", self.secs(), self.millis());
247 }
248}
249
250impl ops::Add<Duration> for Duration {
251 type Output = Duration;
252
253 fn add(self, rhs: Duration) -> Duration {
254 Duration::from_micros(self.micros + rhs.total_micros())
255 }
256}
257
258impl ops::AddAssign<Duration> for Duration {
259 fn add_assign(&mut self, rhs: Duration) {
260 self.micros += rhs.total_micros();
261 }
262}
263
264impl ops::Sub<Duration> for Duration {
265 type Output = Duration;
266
267 fn sub(self, rhs: Duration) -> Duration {
268 Duration::from_micros(
269 self.micros
270 .checked_sub(rhs.total_micros())
271 .expect("overflow when subtracting durations"),
272 )
273 }
274}
275
276impl ops::SubAssign<Duration> for Duration {
277 fn sub_assign(&mut self, rhs: Duration) {
278 self.micros = self
279 .micros
280 .checked_sub(rhs.total_micros())
281 .expect("overflow when subtracting durations");
282 }
283}
284
285impl ops::Mul<u32> for Duration {
286 type Output = Duration;
287
288 fn mul(self, rhs: u32) -> Duration {
289 Duration::from_micros(self.micros * rhs as u64)
290 }
291}
292
293impl ops::MulAssign<u32> for Duration {
294 fn mul_assign(&mut self, rhs: u32) {
295 self.micros *= rhs as u64;
296 }
297}
298
299impl ops::Div<u32> for Duration {
300 type Output = Duration;
301
302 fn div(self, rhs: u32) -> Duration {
303 Duration::from_micros(self.micros / rhs as u64)
304 }
305}
306
307impl ops::DivAssign<u32> for Duration {
308 fn div_assign(&mut self, rhs: u32) {
309 self.micros /= rhs as u64;
310 }
311}
312
313impl ops::Shl<u32> for Duration {
314 type Output = Duration;
315
316 fn shl(self, rhs: u32) -> Duration {
317 Duration::from_micros(self.micros << rhs)
318 }
319}
320
321impl ops::ShlAssign<u32> for Duration {
322 fn shl_assign(&mut self, rhs: u32) {
323 self.micros <<= rhs;
324 }
325}
326
327impl ops::Shr<u32> for Duration {
328 type Output = Duration;
329
330 fn shr(self, rhs: u32) -> Duration {
331 Duration::from_micros(self.micros >> rhs)
332 }
333}
334
335impl ops::ShrAssign<u32> for Duration {
336 fn shr_assign(&mut self, rhs: u32) {
337 self.micros >>= rhs;
338 }
339}
340
341impl From<::core::time::Duration> for Duration {
342 fn from(other: ::core::time::Duration) -> Duration {
343 Duration::from_micros(other.as_secs() * 1000000 + other.subsec_micros() as u64)
344 }
345}
346
347impl From<Duration> for ::core::time::Duration {
348 fn from(val: Duration) -> Self {
349 ::core::time::Duration::from_micros(val.total_micros())
350 }
351}
352
353#[cfg(test)]
354mod test {
355 use super::*;
356
357 #[test]
358 fn test_instant_ops() {
359 assert_eq!(
361 Instant::from_millis(4) + Duration::from_millis(6),
362 Instant::from_millis(10)
363 );
364 assert_eq!(
366 Instant::from_millis(7) - Duration::from_millis(5),
367 Instant::from_millis(2)
368 );
369 }
370
371 #[test]
372 fn test_instant_getters() {
373 let instant = Instant::from_millis(5674);
374 assert_eq!(instant.secs(), 5);
375 assert_eq!(instant.millis(), 674);
376 assert_eq!(instant.total_millis(), 5674);
377 }
378
379 #[test]
380 fn test_instant_display() {
381 assert_eq!(format!("{}", Instant::from_millis(74)), "0.074s");
382 assert_eq!(format!("{}", Instant::from_millis(5674)), "5.674s");
383 assert_eq!(format!("{}", Instant::from_millis(5000)), "5.000s");
384 }
385
386 #[test]
387 #[cfg(feature = "std")]
388 fn test_instant_conversions() {
389 let mut epoc: ::std::time::SystemTime = Instant::from_millis(0).into();
390 assert_eq!(
391 Instant::from(::std::time::UNIX_EPOCH),
392 Instant::from_millis(0)
393 );
394 assert_eq!(epoc, ::std::time::UNIX_EPOCH);
395 epoc = Instant::from_millis(2085955200i64 * 1000).into();
396 assert_eq!(
397 epoc,
398 ::std::time::UNIX_EPOCH + ::std::time::Duration::from_secs(2085955200)
399 );
400 }
401
402 #[test]
403 #[cfg(feature = "std")]
404 fn test_instant_conversions_from_std_instant() {
405 let std_now = ::std::time::Instant::now();
406
407 let before = Instant::from(std_now);
408 ::std::thread::sleep(::std::time::Duration::from_millis(5));
409 let after = Instant::from(std_now);
410
411 assert_eq!(
412 before, after,
413 "converting the same std Instant twice should yield the same result"
414 );
415 }
416
417 #[test]
418 fn test_duration_ops() {
419 assert_eq!(
421 Duration::from_millis(40) + Duration::from_millis(2),
422 Duration::from_millis(42)
423 );
424 assert_eq!(
426 Duration::from_millis(555) - Duration::from_millis(42),
427 Duration::from_millis(513)
428 );
429 assert_eq!(Duration::from_millis(13) * 22, Duration::from_millis(286));
431 assert_eq!(Duration::from_millis(53) / 4, Duration::from_micros(13250));
433 }
434
435 #[test]
436 fn test_duration_assign_ops() {
437 let mut duration = Duration::from_millis(4735);
438 duration += Duration::from_millis(1733);
439 assert_eq!(duration, Duration::from_millis(6468));
440 duration -= Duration::from_millis(1234);
441 assert_eq!(duration, Duration::from_millis(5234));
442 duration *= 4;
443 assert_eq!(duration, Duration::from_millis(20936));
444 duration /= 5;
445 assert_eq!(duration, Duration::from_micros(4187200));
446 }
447
448 #[test]
449 #[should_panic(expected = "overflow when subtracting durations")]
450 fn test_sub_from_zero_overflow() {
451 let _ = Duration::from_millis(0) - Duration::from_millis(1);
452 }
453
454 #[test]
455 #[should_panic(expected = "attempt to divide by zero")]
456 fn test_div_by_zero() {
457 let _ = Duration::from_millis(4) / 0;
458 }
459
460 #[test]
461 fn test_duration_getters() {
462 let instant = Duration::from_millis(4934);
463 assert_eq!(instant.secs(), 4);
464 assert_eq!(instant.millis(), 934);
465 assert_eq!(instant.total_millis(), 4934);
466 }
467
468 #[test]
469 fn test_duration_conversions() {
470 let mut std_duration = ::core::time::Duration::from_millis(4934);
471 let duration: Duration = std_duration.into();
472 assert_eq!(duration, Duration::from_millis(4934));
473 assert_eq!(Duration::from(std_duration), Duration::from_millis(4934));
474
475 std_duration = duration.into();
476 assert_eq!(std_duration, ::core::time::Duration::from_millis(4934));
477 }
478}