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
167 .saturating_add(self.monotonic_millis())
168 }
169}