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}