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}