monotonic_time_rs/lib.rs
1/*
2 * Copyright (c) Peter Bjorklund. All rights reserved. https://github.com/piot/monotonic-time-rs
3 * Licensed under the MIT License. See LICENSE in the project root for license information.
4 */
5pub mod wasm;
6
7use std::fmt;
8use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
9use std::time::{Duration, Instant};
10
11#[cfg(feature = "generic-numerics")]
12pub mod num;
13
14/// Represents a monotonic absolute timestamp with millisecond resolution.
15///
16/// This struct encapsulates a `u64` value representing the number of milliseconds since a
17/// implementation specific epoch.
18#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
19pub struct Millis(u64);
20
21impl Millis {
22 /// Creates a new `Millis` instance from an absolute time in milliseconds.
23 ///
24 /// # Arguments
25 ///
26 /// * `absolute_time` - The absolute time in milliseconds.
27 ///
28 /// # Examples
29 ///
30 /// ```
31 /// use monotonic_time_rs::Millis;
32 /// let timestamp = Millis::new(1_614_834_000);
33 /// ```
34 #[inline]
35 #[must_use] pub const fn new(absolute_time: u64) -> Self {
36 Self(absolute_time)
37 }
38
39 /// Returns the underlying milliseconds value.
40 ///
41 /// # Examples
42 ///
43 /// ```
44 /// use monotonic_time_rs::Millis;
45 /// let timestamp = Millis::new(1_614_834_000);
46 /// assert_eq!(timestamp.absolute_milliseconds(), 1_614_834_000);
47 /// ```
48 #[inline]
49 #[must_use] pub const fn absolute_milliseconds(&self) -> u64 {
50 self.0
51 }
52
53 /// Extracts the lower 16 bits from the timestamp.
54 ///
55 /// This is useful for efficient serialization scenarios where only a subset of the timestamp
56 /// is needed.
57 ///
58 /// # Examples
59 ///
60 /// ```
61 /// use monotonic_time_rs::Millis;
62 /// let timestamp = Millis::new(0x12345678);
63 /// let lower_bits = timestamp.to_lower();
64 /// assert_eq!(lower_bits, 0x5678);
65 /// ```
66 #[must_use] pub const fn to_lower(&self) -> MillisLow16 {
67 (self.0 & 0xffff) as u16
68 }
69
70 /// Reconstructs the full monotonic timestamp from the current time and lower bits.
71 ///
72 /// If the lower bits indicate a wrap-around, adjusts the timestamp accordingly.
73 ///
74 /// # Arguments
75 ///
76 /// * `lower_bits` - The lower 16 bits of a previously recorded timestamp.
77 ///
78 /// # Returns
79 ///
80 /// * `Some(Millis)` - The reconstructed monotonic timestamp if the difference is within 3000 milliseconds.
81 /// * `None` - If the difference between `now` and the reconstructed time exceeds 3000 milliseconds.
82 ///
83 /// # Examples
84 ///
85 /// ```
86 /// use monotonic_time_rs::Millis;
87 /// let current = Millis::new(0x00010000);
88 /// let lower = current.to_lower();
89 /// let reconstructed = current.from_lower(lower).unwrap();
90 /// assert_eq!(reconstructed, current);
91 /// ```
92 #[must_use] pub fn from_lower(&self, lower_bits: MillisLow16) -> Option<Self> {
93 let now_bits = (self.0 & 0xffff) as u16;
94 let received_lower_bits = lower_bits;
95 let top: u64 = self.0 & 0xffffffffffff0000;
96
97 let mut received_monotonic = top | u64::from(received_lower_bits);
98
99 // Adjust for wrap-around if lower bits have wrapped
100 if received_lower_bits > now_bits {
101 received_monotonic = received_monotonic.wrapping_sub(0x10000);
102 }
103
104 let diff = self.0.wrapping_sub(received_monotonic);
105
106 if diff > 3000 {
107 return None;
108 }
109
110 Some(Self::new(received_monotonic))
111 }
112
113 /// Calculates the duration since another `Millis`.
114 ///
115 /// # Arguments
116 ///
117 /// * `earlier` - The earlier monotonic timestamp.
118 ///
119 /// # Returns
120 ///
121 /// A `Duration` representing the elapsed time.
122 ///
123 /// # Panics
124 ///
125 /// Panics if `self` is earlier than `earlier`.
126 ///
127 /// # Examples
128 ///
129 /// ```
130 /// use monotonic_time_rs::Millis;
131 /// use std::time::Duration;
132 /// let start = Millis::new(1000);
133 /// let end = Millis::new(5000);
134 /// let duration = end.duration_since(start);
135 /// assert_eq!(duration, Duration::from_millis(4000));
136 /// ```
137 #[must_use] pub const fn duration_since(&self, earlier: Self) -> Duration {
138 self.checked_duration_since(earlier)
139 .expect("Millis::duration_since called with a later timestamp")
140 }
141
142 /// Calculates the duration since another `Millis`, returning `None` if `self` is earlier.
143 ///
144 /// # Arguments
145 ///
146 /// * `earlier` - The earlier monotonic timestamp.
147 ///
148 /// # Returns
149 ///
150 /// * `Some(Duration)` - The elapsed time if `self` is later than or equal to `earlier`.
151 /// * `None` - If `self` is earlier than `earlier`.
152 ///
153 /// # Examples
154 ///
155 /// ```
156 /// use monotonic_time_rs::Millis;
157 /// use std::time::Duration;
158 /// let start = Millis::new(1000);
159 /// let end = Millis::new(5000);
160 /// assert_eq!(end.checked_duration_since(start), Some(Duration::from_millis(4000)));
161 /// ```
162 #[must_use] pub const fn checked_duration_since(&self, earlier: Self) -> Option<Duration> {
163 if self.0 >= earlier.0 {
164 Some(Duration::from_millis(self.0 - earlier.0))
165 } else {
166 None
167 }
168 }
169
170 /// Calculates the duration since another `Millis`, returning `None` if `self` is earlier.
171 ///
172 /// # Arguments
173 ///
174 /// * `earlier` - The earlier monotonic timestamp.
175 ///
176 /// # Returns
177 ///
178 /// * `Some(MillisDuration)` - The elapsed time in milliseconds if `self` is later than or equal to `earlier`.
179 /// * `None` - If `self` is earlier than `earlier`.
180 ///
181 /// # Examples
182 ///
183 /// ```
184 /// use monotonic_time_rs::Millis;
185 /// let start = Millis::new(1000);
186 /// let end = Millis::new(5000);
187 /// let duration = end.checked_duration_since_ms(start).unwrap();
188 /// assert_eq!(duration.as_millis(), 4000);
189 /// ```
190 #[must_use] pub const fn checked_duration_since_ms(&self, earlier: Self) -> Option<MillisDuration> {
191 if self.0 >= earlier.0 {
192 Some(MillisDuration::from_millis(self.0 - earlier.0))
193 } else {
194 None
195 }
196 }
197
198 /// Calculates the duration since another `Millis`, panicking if `self` is earlier.
199 ///
200 /// # Arguments
201 ///
202 /// * `earlier` - The earlier monotonic timestamp.
203 ///
204 /// # Returns
205 ///
206 /// A `MillisDuration` representing the elapsed time.
207 ///
208 /// # Panics
209 ///
210 /// Panics if `self` is earlier than `earlier`.
211 ///
212 /// # Examples
213 ///
214 /// ```
215 /// use monotonic_time_rs::Millis;
216 /// let start = Millis::new(1000);
217 /// let end = Millis::new(5000);
218 /// let duration = end.duration_since_ms(start);
219 /// assert_eq!(duration.as_millis(), 4000);
220 /// ```
221 #[must_use] pub const fn duration_since_ms(&self, earlier: Self) -> MillisDuration {
222 self.checked_duration_since_ms(earlier)
223 .expect("Millis::duration_since_ms called with a later timestamp")
224 }
225}
226
227impl AddAssign<MillisDuration> for Millis {
228 fn add_assign(&mut self, other: MillisDuration) {
229 self.0 += other.0;
230 }
231}
232
233impl SubAssign<MillisDuration> for Millis {
234 fn sub_assign(&mut self, other: MillisDuration) {
235 self.0 -= other.0;
236 }
237}
238
239impl Add<MillisDuration> for Millis {
240 type Output = Self;
241
242 fn add(self, other: MillisDuration) -> Self::Output {
243 Self(self.0 + other.0)
244 }
245}
246
247impl Sub<MillisDuration> for Millis {
248 type Output = Self;
249
250 fn sub(self, other: MillisDuration) -> Self::Output {
251 Self(self.0 - other.0)
252 }
253}
254
255/// Represents the lower 16 bits of a timestamp in milliseconds.
256///
257/// This type alias is used for efficient serialization scenarios where only a subset of the
258/// timestamp is needed.
259pub type MillisLow16 = u16;
260
261/// Represents a duration in milliseconds.
262#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
263pub struct MillisDuration(u64);
264
265impl MillisDuration {
266 /// Creates a new `MillisDuration` instance from milliseconds.
267 ///
268 /// # Arguments
269 ///
270 /// * `millis` - The duration in milliseconds.
271 ///
272 /// # Examples
273 ///
274 /// ```
275 /// use monotonic_time_rs::MillisDuration;
276 /// let duration = MillisDuration::from_millis(4000);
277 /// ```
278 #[inline]
279 #[must_use] pub const fn from_millis(millis: u64) -> Self {
280 Self(millis)
281 }
282
283 /// Creates a new `MillisDuration` from a number of seconds.
284 /// Returns an error if the input is negative.
285 ///
286 /// # Examples
287 ///
288 /// ```
289 /// use monotonic_time_rs::MillisDuration;
290 /// let duration = MillisDuration::from_secs(2.5).unwrap();
291 /// assert_eq!(duration.as_millis(), 2500);
292 /// ```
293 #[inline]
294 pub fn from_secs(seconds: f32) -> Result<Self, &'static str> {
295 if seconds < 0.0 {
296 return Err("must be a positive value");
297 }
298 Ok(Self((seconds * 1000.0) as u64))
299 }
300
301 /// Returns the duration in milliseconds.
302 ///
303 /// # Examples
304 ///
305 /// ```
306 /// use monotonic_time_rs::MillisDuration;
307 /// let duration = MillisDuration::from_millis(4000);
308 /// assert_eq!(duration.as_millis(), 4000);
309 /// ```
310 #[inline]
311 #[must_use] pub const fn as_millis(&self) -> u64 {
312 self.0
313 }
314
315 #[must_use] pub fn as_secs(&self) -> f32 {
316 self.0 as f32 / 1000.0
317 }
318}
319
320impl fmt::Display for MillisDuration {
321 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
322 write!(f, "{} ms", self.0)
323 }
324}
325
326impl From<u64> for MillisDuration {
327 #[inline]
328 fn from(ms: u64) -> Self {
329 Self::from_millis(ms)
330 }
331}
332
333impl From<MillisDuration> for u64 {
334 #[inline]
335 fn from(duration: MillisDuration) -> Self {
336 duration.0
337 }
338}
339
340impl Mul<f32> for MillisDuration {
341 type Output = Self;
342
343 fn mul(self, rhs: f32) -> Self::Output {
344 Self::from_millis(((self.0 as f32) * rhs) as u64)
345 }
346}
347
348impl Mul<MillisDuration> for f32 {
349 type Output = MillisDuration;
350
351 fn mul(self, rhs: MillisDuration) -> Self::Output {
352 MillisDuration::from_millis((self * (rhs.0 as Self)) as u64)
353 }
354}
355
356impl Mul<u32> for MillisDuration {
357 type Output = Self;
358 #[inline]
359 fn mul(self, rhs: u32) -> Self::Output {
360 Self::from_millis(u64::from((self.0 as u32) * rhs))
361 }
362}
363
364impl Mul<MillisDuration> for u32 {
365 type Output = MillisDuration;
366
367 #[inline]
368 fn mul(self, rhs: MillisDuration) -> Self::Output {
369 MillisDuration::from_millis(u64::from(self * (rhs.0 as Self)))
370 }
371}
372
373impl Add for MillisDuration {
374 type Output = Self;
375
376 #[inline]
377 fn add(self, rhs: Self) -> Self {
378 Self::from_millis(
379 self.0
380 .checked_add(rhs.0)
381 .expect("overflow on add millisduration"),
382 )
383 }
384}
385
386impl AddAssign for MillisDuration {
387 #[inline]
388 fn add_assign(&mut self, rhs: Self) {
389 *self = *self + rhs;
390 }
391}
392
393impl Sub for MillisDuration {
394 type Output = Self;
395
396 #[inline]
397 fn sub(self, rhs: Self) -> Self {
398 Self::from_millis(
399 self.0
400 .checked_sub(rhs.0)
401 .expect("overflow on sub millisduration"),
402 )
403 }
404}
405
406impl SubAssign for MillisDuration {
407 #[inline]
408 fn sub_assign(&mut self, rhs: Self) {
409 *self = *self - rhs;
410 }
411}
412
413impl MulAssign<u32> for MillisDuration {
414 #[inline]
415 fn mul_assign(&mut self, rhs: u32) {
416 *self = *self * rhs;
417 }
418}
419
420impl Div<u32> for MillisDuration {
421 type Output = Self;
422
423 #[inline]
424 fn div(self, rhs: u32) -> Self {
425 Self::from_millis(
426 self.0
427 .checked_div(u64::from(rhs))
428 .expect("divide by zero error millisduration"),
429 )
430 }
431}
432
433impl DivAssign<u32> for MillisDuration {
434 #[inline]
435 fn div_assign(&mut self, rhs: u32) {
436 *self = *self / rhs;
437 }
438}
439
440impl Div<u64> for MillisDuration {
441 type Output = Self;
442
443 #[inline]
444 fn div(self, rhs: u64) -> Self {
445 Self::from_millis(
446 self.0
447 .checked_div(rhs)
448 .expect("divide by zero error millisduration"),
449 )
450 }
451}
452
453impl DivAssign<u64> for MillisDuration {
454 #[inline]
455 fn div_assign(&mut self, rhs: u64) {
456 *self = *self / rhs;
457 }
458}
459
460impl Div<Self> for MillisDuration {
461 type Output = Self;
462
463 fn div(self, rhs: Self) -> Self::Output {
464 self / rhs.0
465 }
466}
467
468impl DivAssign<Self> for MillisDuration {
469 #[inline]
470 fn div_assign(&mut self, rhs: Self) {
471 *self = *self / rhs;
472 }
473}
474
475/// Implements subtraction between two `Millis` instances, returning a `MillisDuration`.
476///
477/// # Panics
478///
479/// Panics if the first timestamp (`self`) is less than the second timestamp (`other`).
480///
481/// # Examples
482///
483/// ```
484/// use monotonic_time_rs::Millis;
485/// let start = Millis::new(1000);
486/// let end = Millis::new(5000);
487/// let duration = end - start;
488/// assert_eq!(duration.as_millis(), 4000);
489/// ```
490impl Sub for Millis {
491 type Output = MillisDuration;
492
493 fn sub(self, other: Self) -> MillisDuration {
494 if self.0 >= other.0 {
495 MillisDuration::from_millis(self.0 - other.0)
496 } else {
497 panic!(
498 "Attempted to subtract a later Millis from an earlier one: {self:?} - {other:?}"
499 );
500 }
501 }
502}
503
504impl From<u64> for Millis {
505 #[inline]
506 fn from(ms: u64) -> Self {
507 Self::new(ms)
508 }
509}
510
511impl From<Millis> for u64 {
512 #[inline]
513 fn from(millis: Millis) -> Self {
514 millis.0
515 }
516}
517
518impl fmt::Display for Millis {
519 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
520 write!(f, "{} ms", self.0)
521 }
522}
523
524/// A trait for providing monotonic time measurements.
525///
526/// Implementors of this trait should provide a method to retrieve the current
527/// monotonic time in milliseconds. Monotonic time is guaranteed to be non-decreasing
528/// and is not affected by system clock updates.
529///
530/// # Examples
531///
532/// ```
533/// use monotonic_time_rs::{MonotonicClock, Millis};
534/// struct SystemClock;
535///
536/// impl MonotonicClock for SystemClock {
537/// fn now(&self) -> Millis {
538/// Millis::new(1_614_834_000)
539/// }
540/// }
541/// ```
542pub trait MonotonicClock {
543 /// Returns the current monotonic time as a `Millis` instance.
544 ///
545 /// # Examples
546 ///
547 /// ```
548 /// use monotonic_time_rs::{MonotonicClock, Millis};
549 /// struct SystemClock;
550 ///
551 /// impl MonotonicClock for SystemClock {
552 /// fn now(&self) -> Millis {
553 /// Millis::new(1_614_834_000)
554 /// }
555 /// }
556 /// ```
557 fn now(&self) -> Millis;
558}
559
560/// A concrete implementation of `MonotonicClock` using `std::time::Instant`.
561///
562/// This struct captures the instant when it was created and provides
563/// the elapsed time since then as a `Millis` timestamp.
564pub struct InstantMonotonicClock {
565 started: Instant,
566}
567
568impl InstantMonotonicClock {
569 /// Creates a new `InstantMonotonicClock` instance, capturing the current instant.
570 ///
571 /// # Examples
572 ///
573 /// ```
574 /// use monotonic_time_rs::InstantMonotonicClock;
575 /// let clock = InstantMonotonicClock::new();
576 /// ```
577 #[must_use] pub fn new() -> Self {
578 Self {
579 started: Instant::now(),
580 }
581 }
582}
583
584impl Default for InstantMonotonicClock {
585 fn default() -> Self {
586 Self::new()
587 }
588}
589
590impl MonotonicClock for InstantMonotonicClock {
591 /// Returns the elapsed monotonic time since the creation of the `InstantMonotonicClock`.
592 ///
593 /// # Examples
594 ///
595 /// ```
596 /// use monotonic_time_rs::{Millis, MonotonicClock, InstantMonotonicClock};
597 /// let clock = InstantMonotonicClock::new();
598 /// std::thread::sleep(std::time::Duration::from_millis(500));
599 /// let current_time = clock.now();
600 /// assert!(current_time.absolute_milliseconds() >= 500);
601 /// ```
602 fn now(&self) -> Millis {
603 let duration = Instant::now().duration_since(self.started);
604 Millis::new(duration.as_millis() as u64)
605 }
606}
607
608#[must_use] pub fn create_monotonic_clock() -> impl MonotonicClock {
609 #[cfg(target_arch = "wasm32")]
610 use crate::wasm::WasmMonotonicClock;
611 #[cfg(target_arch = "wasm32")]
612 {
613 WasmMonotonicClock::new()
614 }
615 #[cfg(not(target_arch = "wasm32"))]
616 {
617 InstantMonotonicClock::new()
618 }
619}