Skip to main content

qubit_clock/
nano_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//! High-precision monotonic clock implementation.
11//!
12//! This module provides [`NanoMonotonicClock`], a clock implementation that
13//! provides nanosecond-precision monotonic time measurements.
14//!
15
16use crate::{
17    Clock,
18    NanoClock,
19};
20use chrono::Utc;
21use std::time::{
22    Duration,
23    Instant,
24};
25
26/// A clock implementation that provides nanosecond-precision monotonic time.
27///
28/// This clock combines the monotonic guarantees of
29/// [`MonotonicClock`](crate::MonotonicClock) with the nanosecond precision
30/// of [`NanoClock`]. It uses `std::time::Instant` as its time source and
31/// stores the base time with nanosecond precision.
32///
33/// # Use Cases
34///
35/// - High-precision performance testing
36/// - Microbenchmarking
37/// - Scenarios requiring nanosecond-level time measurements
38///
39/// # Thread Safety
40///
41/// This type is completely thread-safe as all fields are immutable after
42/// creation.
43///
44/// # Examples
45///
46/// ```
47/// use qubit_clock::{NanoClock, NanoMonotonicClock};
48///
49/// let clock = NanoMonotonicClock::new();
50/// let start = clock.nanos();
51///
52/// // Perform some operation
53/// for _ in 0..1000 {
54///     // Some work
55/// }
56///
57/// let elapsed = clock.nanos() - start;
58/// println!("Elapsed: {} ns", elapsed);
59/// ```
60///
61#[derive(Debug, Clone)]
62pub struct NanoMonotonicClock {
63    /// The base instant when this clock was created.
64    instant_base: Instant,
65    /// The system time (seconds part) when this clock was created.
66    system_time_base_seconds: i64,
67    /// The system time (nanoseconds part) when this clock was created.
68    system_time_base_nanos: u32,
69}
70
71impl NanoMonotonicClock {
72    /// Creates a new `NanoMonotonicClock`.
73    ///
74    /// The clock records the current instant and system time (with nanosecond
75    /// precision) as its base point. All subsequent time queries will be
76    /// calculated relative to this base point.
77    ///
78    /// # Returns
79    ///
80    /// A new `NanoMonotonicClock` instance.
81    ///
82    /// # Examples
83    ///
84    /// ```
85    /// use qubit_clock::NanoMonotonicClock;
86    ///
87    /// let clock = NanoMonotonicClock::new();
88    /// ```
89    ///
90    #[inline]
91    pub fn new() -> Self {
92        let now = Utc::now();
93        NanoMonotonicClock {
94            instant_base: Instant::now(),
95            system_time_base_seconds: now.timestamp(),
96            system_time_base_nanos: now.timestamp_subsec_nanos(),
97        }
98    }
99
100    /// Returns the elapsed monotonic duration since this clock was created.
101    ///
102    /// This value is based purely on `Instant` and is not affected by system
103    /// time adjustments.
104    ///
105    /// # Returns
106    ///
107    /// The elapsed monotonic duration.
108    ///
109    /// # Examples
110    ///
111    /// ```
112    /// use qubit_clock::NanoMonotonicClock;
113    /// use std::thread;
114    /// use std::time::Duration;
115    ///
116    /// let clock = NanoMonotonicClock::new();
117    /// thread::sleep(Duration::from_millis(10));
118    /// assert!(clock.elapsed() >= Duration::from_millis(10));
119    /// ```
120    #[inline]
121    pub fn elapsed(&self) -> Duration {
122        self.instant_base.elapsed()
123    }
124
125    /// Returns the elapsed monotonic time in nanoseconds since creation.
126    ///
127    /// Unlike [`NanoClock::nanos`](crate::NanoClock::nanos), this value does
128    /// not include a wall-clock epoch anchor and is intended for interval
129    /// measurement.
130    ///
131    /// # Returns
132    ///
133    /// The elapsed monotonic nanoseconds, saturated at `i128::MAX`.
134    ///
135    /// # Examples
136    ///
137    /// ```
138    /// use qubit_clock::NanoMonotonicClock;
139    /// use std::thread;
140    /// use std::time::Duration;
141    ///
142    /// let clock = NanoMonotonicClock::new();
143    /// thread::sleep(Duration::from_millis(10));
144    /// assert!(clock.monotonic_nanos() >= 10_000_000);
145    /// ```
146    #[inline]
147    pub fn monotonic_nanos(&self) -> i128 {
148        let elapsed_nanos = self.elapsed().as_nanos();
149        i128::try_from(elapsed_nanos).unwrap_or(i128::MAX)
150    }
151}
152
153impl Default for NanoMonotonicClock {
154    #[inline]
155    fn default() -> Self {
156        Self::new()
157    }
158}
159
160impl Clock for NanoMonotonicClock {
161    #[inline]
162    fn millis(&self) -> i64 {
163        let millis = self.nanos().div_euclid(1_000_000);
164        i64::try_from(millis).unwrap_or(i64::MAX)
165    }
166}
167
168impl NanoClock for NanoMonotonicClock {
169    #[inline]
170    fn nanos(&self) -> i128 {
171        let elapsed_nanos = self.monotonic_nanos();
172        let base_nanos = (self.system_time_base_seconds as i128) * 1_000_000_000
173            + (self.system_time_base_nanos as i128);
174        base_nanos.saturating_add(elapsed_nanos)
175    }
176}