Skip to main content

qubit_clock/
monotonic.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025 - 2026.
4 *    Haixing Hu, Qubit Co. Ltd.
5 *
6 *    All rights reserved.
7 *
8 ******************************************************************************/
9//! Monotonic clock implementation.
10//!
11//! This module provides [`MonotonicClock`], a clock implementation that
12//! guarantees monotonically increasing time values, unaffected by system time
13//! adjustments.
14//!
15//! # Author
16//!
17//! Haixing Hu
18
19use crate::Clock;
20use chrono::Utc;
21use std::time::{Duration, Instant};
22
23/// A clock implementation that provides monotonically increasing time.
24///
25/// This clock uses `std::time::Instant` as its time source, which guarantees
26/// that time always moves forward and is not affected by system time
27/// adjustments (e.g., NTP synchronization, manual changes).
28///
29/// The clock records a base point when created, and all subsequent time
30/// queries are calculated relative to this base point.
31///
32/// # Use Cases
33///
34/// - Performance monitoring
35/// - Timeout control
36/// - Measuring time intervals
37/// - Any scenario requiring stable, monotonic time
38///
39/// # Note
40///
41/// This clock is designed for measuring time intervals, not for getting the
42/// "current time" for display purposes. For timezone support, you can wrap it
43/// with [`Zoned`](crate::Zoned), but this is generally not recommended as
44/// timezone information is not meaningful for interval measurements.
45///
46/// # Thread Safety
47///
48/// This type is completely thread-safe as all fields are immutable after
49/// creation.
50///
51/// # Examples
52///
53/// ```
54/// use qubit_clock::{Clock, MonotonicClock};
55/// use std::thread;
56/// use std::time::Duration;
57///
58/// let clock = MonotonicClock::new();
59/// let start = clock.millis();
60///
61/// thread::sleep(Duration::from_millis(100));
62///
63/// let elapsed = clock.millis() - start;
64/// assert!(elapsed >= 100);
65/// ```
66///
67/// # Author
68///
69/// Haixing Hu
70#[derive(Debug, Clone)]
71pub struct MonotonicClock {
72    /// The base instant when this clock was created.
73    instant_base: Instant,
74    /// The system time (in milliseconds) when this clock was created.
75    system_time_base_millis: i64,
76}
77
78impl MonotonicClock {
79    /// Creates a new `MonotonicClock`.
80    ///
81    /// The clock records the current instant and system time as its base
82    /// point. All subsequent time queries will be calculated relative to this
83    /// base point.
84    ///
85    /// # Returns
86    ///
87    /// A new `MonotonicClock` instance.
88    ///
89    /// # Examples
90    ///
91    /// ```
92    /// use qubit_clock::MonotonicClock;
93    ///
94    /// let clock = MonotonicClock::new();
95    /// ```
96    ///
97    #[inline]
98    pub fn new() -> Self {
99        MonotonicClock {
100            instant_base: Instant::now(),
101            system_time_base_millis: Utc::now().timestamp_millis(),
102        }
103    }
104
105    /// Returns the elapsed monotonic duration since this clock was created.
106    ///
107    /// This value is based purely on `Instant` and is not affected by system
108    /// time adjustments.
109    ///
110    /// # Returns
111    ///
112    /// The elapsed monotonic duration.
113    ///
114    /// # Examples
115    ///
116    /// ```
117    /// use qubit_clock::MonotonicClock;
118    /// use std::thread;
119    /// use std::time::Duration;
120    ///
121    /// let clock = MonotonicClock::new();
122    /// thread::sleep(Duration::from_millis(10));
123    /// assert!(clock.elapsed() >= Duration::from_millis(10));
124    /// ```
125    #[inline]
126    pub fn elapsed(&self) -> Duration {
127        self.instant_base.elapsed()
128    }
129
130    /// Returns the elapsed monotonic time in milliseconds since creation.
131    ///
132    /// Unlike [`Clock::millis`](crate::Clock::millis), this value does not
133    /// include a wall-clock epoch anchor and is intended for interval
134    /// measurement.
135    ///
136    /// # Returns
137    ///
138    /// The elapsed monotonic milliseconds, saturated at `i64::MAX`.
139    ///
140    /// # Examples
141    ///
142    /// ```
143    /// use qubit_clock::MonotonicClock;
144    /// use std::thread;
145    /// use std::time::Duration;
146    ///
147    /// let clock = MonotonicClock::new();
148    /// thread::sleep(Duration::from_millis(10));
149    /// assert!(clock.monotonic_millis() >= 10);
150    /// ```
151    #[inline]
152    pub fn monotonic_millis(&self) -> i64 {
153        let elapsed_millis = self.elapsed().as_millis();
154        if elapsed_millis > i64::MAX as u128 {
155            i64::MAX
156        } else {
157            elapsed_millis as i64
158        }
159    }
160}
161
162impl Default for MonotonicClock {
163    #[inline]
164    fn default() -> Self {
165        Self::new()
166    }
167}
168
169impl Clock for MonotonicClock {
170    #[inline]
171    fn millis(&self) -> i64 {
172        self.system_time_base_millis
173            .saturating_add(self.monotonic_millis())
174    }
175}