Skip to main content

qubit_clock/meter/
time_meter.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025 - 2026.
4 *    Haixing Hu, Qubit Co. Ltd.
5 *
6 *    All rights reserved.
7 *
8 ******************************************************************************/
9//! Millisecond-precision time meter implementation.
10//!
11//! This module provides [`TimeMeter`], a simple yet powerful time
12//! measurement tool with millisecond precision. For nanosecond precision,
13//! use [`NanoTimeMeter`](super::NanoTimeMeter).
14//!
15//! # Author
16//!
17//! Haixing Hu
18
19use crate::meter::format::{format_duration_millis, format_speed};
20use crate::{Clock, MonotonicClock};
21use chrono::Duration;
22
23/// A time meter for measuring elapsed time with millisecond precision.
24///
25/// This meter provides a simple and powerful tool for time measurement
26/// with the following features:
27///
28/// - **Flexible clock source**: Supports any clock implementing `Clock`
29///   trait via generic parameter
30/// - **High precision**: Uses `MonotonicClock` by default, based on
31///   `Instant`
32/// - **Easy to use**: Provides simple start/stop interface
33/// - **Multiple output formats**: Supports milliseconds, seconds, minutes,
34///   and human-readable format
35/// - **Speed calculation**: Provides per-second and per-minute speed
36///   calculation
37/// - **Test-friendly**: Supports injecting `MockClock` for unit testing
38///
39/// # Design Philosophy
40///
41/// `TimeMeter` uses dependency injection pattern through the `Clock`
42/// trait. This design brings the following benefits:
43///
44/// 1. **Production reliability**: Uses `MonotonicClock` by default,
45///    ensuring time measurement is not affected by system time adjustments
46/// 2. **Test controllability**: Can inject `MockClock` for deterministic
47///    time testing
48/// 3. **Extensibility**: Can implement custom `Clock` to meet special
49///    requirements
50/// 4. **Compatibility**: Fully compatible with standard Java time API
51///
52/// # Default Clock Selection
53///
54/// If no clock is specified, this meter uses `MonotonicClock` as the
55/// default clock instead of system clock. This is because:
56///
57/// - `MonotonicClock` is based on `Instant`, providing monotonically
58///   increasing time
59/// - Not affected by system time adjustments (e.g., NTP sync, manual
60///   settings)
61/// - More suitable for performance measurement and benchmarking scenarios
62/// - Provides more stable and reliable results in most use cases
63///
64/// # Thread Safety
65///
66/// This type is not thread-safe. If you need to use it in a
67/// multi-threaded environment, you should create separate instances for
68/// each thread or use external synchronization mechanisms.
69///
70/// # Examples
71///
72/// ```
73/// use qubit_clock::meter::TimeMeter;
74/// use std::thread;
75/// use std::time::Duration as StdDuration;
76///
77/// // Basic usage
78/// let mut meter = TimeMeter::new();
79/// meter.start();
80/// thread::sleep(StdDuration::from_millis(100));
81/// meter.stop();
82/// println!("Elapsed: {}", meter.readable_duration());
83///
84/// // Auto-start
85/// let mut meter = TimeMeter::start_now();
86/// thread::sleep(StdDuration::from_millis(50));
87/// meter.stop();
88///
89/// // Real-time monitoring (without calling stop)
90/// let mut meter = TimeMeter::start_now();
91/// for _ in 0..5 {
92///     thread::sleep(StdDuration::from_millis(10));
93///     println!("Running: {}", meter.readable_duration());
94/// }
95/// ```
96///
97/// # Author
98///
99/// Haixing Hu
100pub struct TimeMeter<C: Clock> {
101    /// The clock used by this meter.
102    clock: C,
103    /// Start timestamp in milliseconds. `None` means not started.
104    start_time: Option<i64>,
105    /// End timestamp in milliseconds. `None` means not stopped.
106    end_time: Option<i64>,
107}
108
109impl<C: Clock> TimeMeter<C> {
110    /// Creates a new time meter with the specified clock.
111    ///
112    /// # Arguments
113    ///
114    /// * `clock` - The clock to use for time measurement
115    ///
116    /// # Returns
117    ///
118    /// A new `TimeMeter` instance
119    ///
120    /// # Examples
121    ///
122    /// ```
123    /// use qubit_clock::{MonotonicClock, meter::TimeMeter};
124    ///
125    /// let clock = MonotonicClock::new();
126    /// let meter = TimeMeter::with_clock(clock);
127    /// ```
128    #[inline]
129    pub fn with_clock(clock: C) -> Self {
130        TimeMeter {
131            clock,
132            start_time: None,
133            end_time: None,
134        }
135    }
136
137    /// Creates a new time meter with the specified clock and starts it
138    /// immediately.
139    ///
140    /// # Arguments
141    ///
142    /// * `clock` - The clock to use for time measurement
143    ///
144    /// # Returns
145    ///
146    /// A new `TimeMeter` instance that has already been started
147    ///
148    /// # Examples
149    ///
150    /// ```
151    /// use qubit_clock::{MonotonicClock, meter::TimeMeter};
152    ///
153    /// let clock = MonotonicClock::new();
154    /// let meter = TimeMeter::with_clock_started(clock);
155    /// ```
156    #[inline]
157    pub fn with_clock_started(clock: C) -> Self {
158        let mut meter = Self::with_clock(clock);
159        meter.start();
160        meter
161    }
162
163    /// Starts this meter.
164    ///
165    /// Records the current time as the start timestamp. If the meter has
166    /// already been started, this operation will restart timing.
167    ///
168    /// # Examples
169    ///
170    /// ```
171    /// use qubit_clock::meter::TimeMeter;
172    ///
173    /// let mut meter = TimeMeter::new();
174    /// meter.start();
175    /// ```
176    #[inline]
177    pub fn start(&mut self) {
178        self.start_time = Some(self.clock.millis());
179        self.end_time = None;
180    }
181
182    /// Stops this meter.
183    ///
184    /// Records the current time as the end timestamp. After calling this
185    /// method, `duration()` will return a fixed time interval until
186    /// `start()` or `reset()` is called again.
187    ///
188    /// # Examples
189    ///
190    /// ```
191    /// use qubit_clock::meter::TimeMeter;
192    ///
193    /// let mut meter = TimeMeter::start_now();
194    /// // Do some work
195    /// meter.stop();
196    /// ```
197    #[inline]
198    pub fn stop(&mut self) {
199        self.end_time = Some(self.clock.millis());
200    }
201
202    /// Resets this meter.
203    ///
204    /// Clears the start and end timestamps, restoring the meter to its
205    /// initial state. After reset, you need to call `start()` again to
206    /// begin a new time measurement.
207    ///
208    /// # Examples
209    ///
210    /// ```
211    /// use qubit_clock::meter::TimeMeter;
212    ///
213    /// let mut meter = TimeMeter::start_now();
214    /// // Do some work
215    /// meter.stop();
216    /// meter.reset();
217    /// ```
218    #[inline]
219    pub fn reset(&mut self) {
220        self.start_time = None;
221        self.end_time = None;
222    }
223
224    /// Resets and immediately starts this meter.
225    ///
226    /// This is equivalent to calling `reset()` followed by `start()`.
227    ///
228    /// # Examples
229    ///
230    /// ```
231    /// use qubit_clock::meter::TimeMeter;
232    ///
233    /// let mut meter = TimeMeter::start_now();
234    /// // Do some work
235    /// meter.restart();
236    /// // Do more work
237    /// ```
238    #[inline]
239    pub fn restart(&mut self) {
240        self.reset();
241        self.start();
242    }
243
244    /// Returns the elapsed duration in milliseconds.
245    ///
246    /// If the meter has been stopped (by calling `stop()`), returns the
247    /// time interval from start to stop. If the meter has not been
248    /// stopped, returns the time interval from start to the current
249    /// moment.
250    ///
251    /// If the meter has not been started (by calling `start()`), returns
252    /// 0.
253    ///
254    /// # Returns
255    ///
256    /// The elapsed duration in milliseconds
257    ///
258    /// # Examples
259    ///
260    /// ```
261    /// use qubit_clock::meter::TimeMeter;
262    /// use std::thread;
263    /// use std::time::Duration;
264    ///
265    /// let mut meter = TimeMeter::start_now();
266    /// thread::sleep(Duration::from_millis(100));
267    /// assert!(meter.millis() >= 100);
268    /// ```
269    #[inline]
270    pub fn millis(&self) -> i64 {
271        let start = match self.start_time {
272            Some(t) => t,
273            None => return 0,
274        };
275        let end = self.end_time.unwrap_or_else(|| self.clock.millis());
276        end - start
277    }
278
279    /// Returns the elapsed duration in seconds.
280    ///
281    /// This method is based on the result of `millis()`, converting
282    /// milliseconds to seconds (rounded down).
283    ///
284    /// # Returns
285    ///
286    /// The elapsed duration in seconds
287    ///
288    /// # Examples
289    ///
290    /// ```
291    /// use qubit_clock::meter::TimeMeter;
292    /// use std::thread;
293    /// use std::time::Duration;
294    ///
295    /// let mut meter = TimeMeter::start_now();
296    /// thread::sleep(Duration::from_secs(2));
297    /// assert!(meter.seconds() >= 2);
298    /// ```
299    #[inline]
300    pub fn seconds(&self) -> i64 {
301        self.millis() / 1000
302    }
303
304    /// Returns the elapsed duration in minutes.
305    ///
306    /// This method is based on the result of `millis()`, converting
307    /// milliseconds to minutes (rounded down).
308    ///
309    /// # Returns
310    ///
311    /// The elapsed duration in minutes
312    ///
313    /// # Examples
314    ///
315    /// ```
316    /// use qubit_clock::meter::TimeMeter;
317    ///
318    /// let mut meter = TimeMeter::new();
319    /// meter.start();
320    /// // Simulate 2 minutes
321    /// meter.stop();
322    /// // In real usage, this would be >= 2 after 2 minutes
323    /// ```
324    #[inline]
325    pub fn minutes(&self) -> i64 {
326        self.millis() / 60000
327    }
328
329    /// Returns the elapsed duration as a `Duration` object.
330    ///
331    /// If the meter has been stopped (by calling `stop()`), returns the
332    /// time interval from start to stop. If the meter has not been
333    /// stopped, returns the time interval from start to the current
334    /// moment.
335    ///
336    /// If the meter has not been started (by calling `start()`), returns
337    /// a zero duration.
338    ///
339    /// # Returns
340    ///
341    /// The elapsed duration as a `Duration` object (millisecond precision)
342    ///
343    /// # Examples
344    ///
345    /// ```
346    /// use qubit_clock::meter::TimeMeter;
347    ///
348    /// let mut meter = TimeMeter::start_now();
349    /// let duration = meter.duration();
350    /// ```
351    #[inline]
352    pub fn duration(&self) -> Duration {
353        Duration::milliseconds(self.millis())
354    }
355
356    /// Returns a human-readable string representation of the elapsed
357    /// duration.
358    ///
359    /// Formats the duration into an easy-to-read string, such as
360    /// "1h 23m 45s" or "2.5s".
361    ///
362    /// # Returns
363    ///
364    /// A human-readable string representation of the duration
365    ///
366    /// # Examples
367    ///
368    /// ```
369    /// use qubit_clock::meter::TimeMeter;
370    ///
371    /// let mut meter = TimeMeter::start_now();
372    /// // Do some work
373    /// meter.stop();
374    /// println!("Elapsed: {}", meter.readable_duration());
375    /// ```
376    #[inline]
377    pub fn readable_duration(&self) -> String {
378        format_duration_millis(self.millis())
379    }
380
381    /// Calculates the per-second speed for a given count.
382    ///
383    /// Computes the average count processed per second during the elapsed
384    /// time. Useful for performance monitoring and speed analysis.
385    ///
386    /// # Arguments
387    ///
388    /// * `count` - The count value to calculate speed for
389    ///
390    /// # Returns
391    ///
392    /// The per-second speed, or `None` if the elapsed time is zero
393    ///
394    /// # Examples
395    ///
396    /// ```
397    /// use qubit_clock::meter::TimeMeter;
398    /// use std::thread;
399    /// use std::time::Duration;
400    ///
401    /// let mut meter = TimeMeter::start_now();
402    /// thread::sleep(Duration::from_secs(1));
403    /// meter.stop();
404    /// if let Some(speed) = meter.speed_per_second(1000) {
405    ///     println!("Speed: {:.2} items/s", speed);
406    /// }
407    /// ```
408    #[inline]
409    pub fn speed_per_second(&self, count: usize) -> Option<f64> {
410        let elapsed_millis = self.millis();
411        if elapsed_millis <= 0 {
412            None
413        } else {
414            Some((count as f64 * 1000.0) / elapsed_millis as f64)
415        }
416    }
417
418    /// Calculates the per-minute speed for a given count.
419    ///
420    /// Computes the average count processed per minute during the elapsed
421    /// time. Useful for performance monitoring and speed analysis.
422    ///
423    /// # Arguments
424    ///
425    /// * `count` - The count value to calculate speed for
426    ///
427    /// # Returns
428    ///
429    /// The per-minute speed, or `None` if the elapsed time is zero
430    ///
431    /// # Examples
432    ///
433    /// ```
434    /// use qubit_clock::meter::TimeMeter;
435    /// use std::thread;
436    /// use std::time::Duration;
437    ///
438    /// let mut meter = TimeMeter::start_now();
439    /// thread::sleep(Duration::from_secs(1));
440    /// meter.stop();
441    /// if let Some(speed) = meter.speed_per_minute(1000) {
442    ///     println!("Speed: {:.2} items/m", speed);
443    /// }
444    /// ```
445    #[inline]
446    pub fn speed_per_minute(&self, count: usize) -> Option<f64> {
447        let elapsed_millis = self.millis();
448        if elapsed_millis <= 0 {
449            None
450        } else {
451            Some((count as f64 * 60_000.0) / elapsed_millis as f64)
452        }
453    }
454
455    /// Returns a formatted string of the per-second speed for a given
456    /// count.
457    ///
458    /// # Arguments
459    ///
460    /// * `count` - The count value to calculate speed for
461    ///
462    /// # Returns
463    ///
464    /// A string in the format "{speed}/s", or "N/A" if the elapsed time
465    /// is zero
466    ///
467    /// # Examples
468    ///
469    /// ```
470    /// use qubit_clock::meter::TimeMeter;
471    ///
472    /// let mut meter = TimeMeter::start_now();
473    /// // Do some work
474    /// meter.stop();
475    /// println!("Speed: {}", meter.formatted_speed_per_second(1000));
476    /// ```
477    #[inline]
478    pub fn formatted_speed_per_second(&self, count: usize) -> String {
479        match self.speed_per_second(count) {
480            Some(speed) => format_speed(speed, "/s"),
481            None => "N/A".to_string(),
482        }
483    }
484
485    /// Returns a formatted string of the per-minute speed for a given
486    /// count.
487    ///
488    /// # Arguments
489    ///
490    /// * `count` - The count value to calculate speed for
491    ///
492    /// # Returns
493    ///
494    /// A string in the format "{speed}/m", or "N/A" if the elapsed time
495    /// is zero
496    ///
497    /// # Examples
498    ///
499    /// ```
500    /// use qubit_clock::meter::TimeMeter;
501    ///
502    /// let mut meter = TimeMeter::start_now();
503    /// // Do some work
504    /// meter.stop();
505    /// println!("Speed: {}", meter.formatted_speed_per_minute(1000));
506    /// ```
507    #[inline]
508    pub fn formatted_speed_per_minute(&self, count: usize) -> String {
509        match self.speed_per_minute(count) {
510            Some(speed) => format_speed(speed, "/m"),
511            None => "N/A".to_string(),
512        }
513    }
514
515    /// Checks if the meter is currently running.
516    ///
517    /// # Returns
518    ///
519    /// `true` if the meter has been started but not stopped, `false`
520    /// otherwise
521    ///
522    /// # Examples
523    ///
524    /// ```
525    /// use qubit_clock::meter::TimeMeter;
526    ///
527    /// let mut meter = TimeMeter::new();
528    /// assert!(!meter.is_running());
529    /// meter.start();
530    /// assert!(meter.is_running());
531    /// meter.stop();
532    /// assert!(!meter.is_running());
533    /// ```
534    #[inline]
535    pub fn is_running(&self) -> bool {
536        self.start_time.is_some() && self.end_time.is_none()
537    }
538
539    /// Checks if the meter has been stopped.
540    ///
541    /// # Returns
542    ///
543    /// `true` if the meter has been stopped, `false` otherwise
544    ///
545    /// # Examples
546    ///
547    /// ```
548    /// use qubit_clock::meter::TimeMeter;
549    ///
550    /// let mut meter = TimeMeter::start_now();
551    /// assert!(!meter.is_stopped());
552    /// meter.stop();
553    /// assert!(meter.is_stopped());
554    /// ```
555    #[inline]
556    pub fn is_stopped(&self) -> bool {
557        self.end_time.is_some()
558    }
559
560    /// Returns a reference to the clock used by this meter.
561    ///
562    /// # Returns
563    ///
564    /// A reference to the clock
565    ///
566    /// # Examples
567    ///
568    /// ```
569    /// use qubit_clock::meter::TimeMeter;
570    ///
571    /// let meter = TimeMeter::new();
572    /// let clock = meter.clock();
573    /// ```
574    #[inline]
575    pub fn clock(&self) -> &C {
576        &self.clock
577    }
578
579    /// Returns a mutable reference to the clock used by this meter.
580    ///
581    /// # Returns
582    ///
583    /// A mutable reference to the clock
584    ///
585    /// # Examples
586    ///
587    /// ```
588    /// use qubit_clock::meter::TimeMeter;
589    ///
590    /// let mut meter = TimeMeter::new();
591    /// let clock = meter.clock_mut();
592    /// ```
593    #[inline]
594    pub fn clock_mut(&mut self) -> &mut C {
595        &mut self.clock
596    }
597}
598
599impl TimeMeter<MonotonicClock> {
600    /// Creates a new time meter using the default `MonotonicClock`.
601    ///
602    /// The default clock uses `MonotonicClock`, which is based on
603    /// `Instant` and is not affected by system time adjustments, making
604    /// it more suitable for time measurement.
605    ///
606    /// # Returns
607    ///
608    /// A new `TimeMeter` instance
609    ///
610    /// # Examples
611    ///
612    /// ```
613    /// use qubit_clock::meter::TimeMeter;
614    ///
615    /// let meter = TimeMeter::new();
616    /// ```
617    #[inline]
618    pub fn new() -> Self {
619        Self::with_clock(MonotonicClock::new())
620    }
621
622    /// Creates a new time meter using the default `MonotonicClock` and
623    /// starts it immediately.
624    ///
625    /// # Returns
626    ///
627    /// A new `TimeMeter` instance that has already been started
628    ///
629    /// # Examples
630    ///
631    /// ```
632    /// use qubit_clock::meter::TimeMeter;
633    ///
634    /// let meter = TimeMeter::start_now();
635    /// ```
636    #[inline]
637    pub fn start_now() -> Self {
638        Self::with_clock_started(MonotonicClock::new())
639    }
640}
641
642impl Default for TimeMeter<MonotonicClock> {
643    #[inline]
644    fn default() -> Self {
645        Self::new()
646    }
647}