Skip to main content

qubit_clock/clock/
monotonic_clock.rs

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