prism3_clock/meter/
time_meter.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025.
4 *    3-Prism 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 prism3_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 prism3_clock::{MonotonicClock, meter::TimeMeter};
124    ///
125    /// let clock = MonotonicClock::new();
126    /// let meter = TimeMeter::with_clock(clock);
127    /// ```
128    pub fn with_clock(clock: C) -> Self {
129        TimeMeter {
130            clock,
131            start_time: None,
132            end_time: None,
133        }
134    }
135
136    /// Creates a new time meter with the specified clock and starts it
137    /// immediately.
138    ///
139    /// # Arguments
140    ///
141    /// * `clock` - The clock to use for time measurement
142    ///
143    /// # Returns
144    ///
145    /// A new `TimeMeter` instance that has already been started
146    ///
147    /// # Examples
148    ///
149    /// ```
150    /// use prism3_clock::{MonotonicClock, meter::TimeMeter};
151    ///
152    /// let clock = MonotonicClock::new();
153    /// let meter = TimeMeter::with_clock_started(clock);
154    /// ```
155    pub fn with_clock_started(clock: C) -> Self {
156        let mut meter = Self::with_clock(clock);
157        meter.start();
158        meter
159    }
160
161    /// Starts this meter.
162    ///
163    /// Records the current time as the start timestamp. If the meter has
164    /// already been started, this operation will restart timing.
165    ///
166    /// # Examples
167    ///
168    /// ```
169    /// use prism3_clock::meter::TimeMeter;
170    ///
171    /// let mut meter = TimeMeter::new();
172    /// meter.start();
173    /// ```
174    pub fn start(&mut self) {
175        self.start_time = Some(self.clock.millis());
176        self.end_time = None;
177    }
178
179    /// Stops this meter.
180    ///
181    /// Records the current time as the end timestamp. After calling this
182    /// method, `duration()` will return a fixed time interval until
183    /// `start()` or `reset()` is called again.
184    ///
185    /// # Examples
186    ///
187    /// ```
188    /// use prism3_clock::meter::TimeMeter;
189    ///
190    /// let mut meter = TimeMeter::start_now();
191    /// // Do some work
192    /// meter.stop();
193    /// ```
194    pub fn stop(&mut self) {
195        self.end_time = Some(self.clock.millis());
196    }
197
198    /// Resets this meter.
199    ///
200    /// Clears the start and end timestamps, restoring the meter to its
201    /// initial state. After reset, you need to call `start()` again to
202    /// begin a new time measurement.
203    ///
204    /// # Examples
205    ///
206    /// ```
207    /// use prism3_clock::meter::TimeMeter;
208    ///
209    /// let mut meter = TimeMeter::start_now();
210    /// // Do some work
211    /// meter.stop();
212    /// meter.reset();
213    /// ```
214    pub fn reset(&mut self) {
215        self.start_time = None;
216        self.end_time = None;
217    }
218
219    /// Resets and immediately starts this meter.
220    ///
221    /// This is equivalent to calling `reset()` followed by `start()`.
222    ///
223    /// # Examples
224    ///
225    /// ```
226    /// use prism3_clock::meter::TimeMeter;
227    ///
228    /// let mut meter = TimeMeter::start_now();
229    /// // Do some work
230    /// meter.restart();
231    /// // Do more work
232    /// ```
233    pub fn restart(&mut self) {
234        self.reset();
235        self.start();
236    }
237
238    /// Returns the elapsed duration in milliseconds.
239    ///
240    /// If the meter has been stopped (by calling `stop()`), returns the
241    /// time interval from start to stop. If the meter has not been
242    /// stopped, returns the time interval from start to the current
243    /// moment.
244    ///
245    /// If the meter has not been started (by calling `start()`), returns
246    /// 0.
247    ///
248    /// # Returns
249    ///
250    /// The elapsed duration in milliseconds
251    ///
252    /// # Examples
253    ///
254    /// ```
255    /// use prism3_clock::meter::TimeMeter;
256    /// use std::thread;
257    /// use std::time::Duration;
258    ///
259    /// let mut meter = TimeMeter::start_now();
260    /// thread::sleep(Duration::from_millis(100));
261    /// assert!(meter.millis() >= 100);
262    /// ```
263    pub fn millis(&self) -> i64 {
264        let start = match self.start_time {
265            Some(t) => t,
266            None => return 0,
267        };
268        let end = self.end_time.unwrap_or_else(|| self.clock.millis());
269        end - start
270    }
271
272    /// Returns the elapsed duration in seconds.
273    ///
274    /// This method is based on the result of `millis()`, converting
275    /// milliseconds to seconds (rounded down).
276    ///
277    /// # Returns
278    ///
279    /// The elapsed duration in seconds
280    ///
281    /// # Examples
282    ///
283    /// ```
284    /// use prism3_clock::meter::TimeMeter;
285    /// use std::thread;
286    /// use std::time::Duration;
287    ///
288    /// let mut meter = TimeMeter::start_now();
289    /// thread::sleep(Duration::from_secs(2));
290    /// assert!(meter.seconds() >= 2);
291    /// ```
292    pub fn seconds(&self) -> i64 {
293        self.millis() / 1000
294    }
295
296    /// Returns the elapsed duration in minutes.
297    ///
298    /// This method is based on the result of `millis()`, converting
299    /// milliseconds to minutes (rounded down).
300    ///
301    /// # Returns
302    ///
303    /// The elapsed duration in minutes
304    ///
305    /// # Examples
306    ///
307    /// ```
308    /// use prism3_clock::meter::TimeMeter;
309    ///
310    /// let mut meter = TimeMeter::new();
311    /// meter.start();
312    /// // Simulate 2 minutes
313    /// meter.stop();
314    /// // In real usage, this would be >= 2 after 2 minutes
315    /// ```
316    pub fn minutes(&self) -> i64 {
317        self.millis() / 60000
318    }
319
320    /// Returns the elapsed duration as a `Duration` object.
321    ///
322    /// If the meter has been stopped (by calling `stop()`), returns the
323    /// time interval from start to stop. If the meter has not been
324    /// stopped, returns the time interval from start to the current
325    /// moment.
326    ///
327    /// If the meter has not been started (by calling `start()`), returns
328    /// a zero duration.
329    ///
330    /// # Returns
331    ///
332    /// The elapsed duration as a `Duration` object (millisecond precision)
333    ///
334    /// # Examples
335    ///
336    /// ```
337    /// use prism3_clock::meter::TimeMeter;
338    ///
339    /// let mut meter = TimeMeter::start_now();
340    /// let duration = meter.duration();
341    /// ```
342    pub fn duration(&self) -> Duration {
343        Duration::milliseconds(self.millis())
344    }
345
346    /// Returns a human-readable string representation of the elapsed
347    /// duration.
348    ///
349    /// Formats the duration into an easy-to-read string, such as
350    /// "1h 23m 45s" or "2.5s".
351    ///
352    /// # Returns
353    ///
354    /// A human-readable string representation of the duration
355    ///
356    /// # Examples
357    ///
358    /// ```
359    /// use prism3_clock::meter::TimeMeter;
360    ///
361    /// let mut meter = TimeMeter::start_now();
362    /// // Do some work
363    /// meter.stop();
364    /// println!("Elapsed: {}", meter.readable_duration());
365    /// ```
366    pub fn readable_duration(&self) -> String {
367        format_duration_millis(self.millis())
368    }
369
370    /// Calculates the per-second speed for a given count.
371    ///
372    /// Computes the average count processed per second during the elapsed
373    /// time. Useful for performance monitoring and speed analysis.
374    ///
375    /// # Arguments
376    ///
377    /// * `count` - The count value to calculate speed for
378    ///
379    /// # Returns
380    ///
381    /// The per-second speed, or `None` if the elapsed time is zero
382    ///
383    /// # Examples
384    ///
385    /// ```
386    /// use prism3_clock::meter::TimeMeter;
387    /// use std::thread;
388    /// use std::time::Duration;
389    ///
390    /// let mut meter = TimeMeter::start_now();
391    /// thread::sleep(Duration::from_secs(1));
392    /// meter.stop();
393    /// if let Some(speed) = meter.speed_per_second(1000) {
394    ///     println!("Speed: {:.2} items/s", speed);
395    /// }
396    /// ```
397    pub fn speed_per_second(&self, count: usize) -> Option<f64> {
398        let seconds = self.seconds();
399        if seconds == 0 {
400            None
401        } else {
402            Some(count as f64 / seconds as f64)
403        }
404    }
405
406    /// Calculates the per-minute speed for a given count.
407    ///
408    /// Computes the average count processed per minute during the elapsed
409    /// time. Useful for performance monitoring and speed analysis.
410    ///
411    /// # Arguments
412    ///
413    /// * `count` - The count value to calculate speed for
414    ///
415    /// # Returns
416    ///
417    /// The per-minute speed, or `None` if the elapsed time is zero
418    ///
419    /// # Examples
420    ///
421    /// ```
422    /// use prism3_clock::meter::TimeMeter;
423    /// use std::thread;
424    /// use std::time::Duration;
425    ///
426    /// let mut meter = TimeMeter::start_now();
427    /// thread::sleep(Duration::from_secs(1));
428    /// meter.stop();
429    /// if let Some(speed) = meter.speed_per_minute(1000) {
430    ///     println!("Speed: {:.2} items/m", speed);
431    /// }
432    /// ```
433    pub fn speed_per_minute(&self, count: usize) -> Option<f64> {
434        let seconds = self.seconds();
435        if seconds == 0 {
436            None
437        } else {
438            Some((count as f64 / seconds as f64) * 60.0)
439        }
440    }
441
442    /// Returns a formatted string of the per-second speed for a given
443    /// count.
444    ///
445    /// # Arguments
446    ///
447    /// * `count` - The count value to calculate speed for
448    ///
449    /// # Returns
450    ///
451    /// A string in the format "{speed}/s", or "N/A" if the elapsed time
452    /// is zero
453    ///
454    /// # Examples
455    ///
456    /// ```
457    /// use prism3_clock::meter::TimeMeter;
458    ///
459    /// let mut meter = TimeMeter::start_now();
460    /// // Do some work
461    /// meter.stop();
462    /// println!("Speed: {}", meter.formatted_speed_per_second(1000));
463    /// ```
464    pub fn formatted_speed_per_second(&self, count: usize) -> String {
465        match self.speed_per_second(count) {
466            Some(speed) => format_speed(speed, "/s"),
467            None => "N/A".to_string(),
468        }
469    }
470
471    /// Returns a formatted string of the per-minute speed for a given
472    /// count.
473    ///
474    /// # Arguments
475    ///
476    /// * `count` - The count value to calculate speed for
477    ///
478    /// # Returns
479    ///
480    /// A string in the format "{speed}/m", or "N/A" if the elapsed time
481    /// is zero
482    ///
483    /// # Examples
484    ///
485    /// ```
486    /// use prism3_clock::meter::TimeMeter;
487    ///
488    /// let mut meter = TimeMeter::start_now();
489    /// // Do some work
490    /// meter.stop();
491    /// println!("Speed: {}", meter.formatted_speed_per_minute(1000));
492    /// ```
493    pub fn formatted_speed_per_minute(&self, count: usize) -> String {
494        match self.speed_per_minute(count) {
495            Some(speed) => format_speed(speed, "/m"),
496            None => "N/A".to_string(),
497        }
498    }
499
500    /// Checks if the meter is currently running.
501    ///
502    /// # Returns
503    ///
504    /// `true` if the meter has been started but not stopped, `false`
505    /// otherwise
506    ///
507    /// # Examples
508    ///
509    /// ```
510    /// use prism3_clock::meter::TimeMeter;
511    ///
512    /// let mut meter = TimeMeter::new();
513    /// assert!(!meter.is_running());
514    /// meter.start();
515    /// assert!(meter.is_running());
516    /// meter.stop();
517    /// assert!(!meter.is_running());
518    /// ```
519    pub fn is_running(&self) -> bool {
520        self.start_time.is_some() && self.end_time.is_none()
521    }
522
523    /// Checks if the meter has been stopped.
524    ///
525    /// # Returns
526    ///
527    /// `true` if the meter has been stopped, `false` otherwise
528    ///
529    /// # Examples
530    ///
531    /// ```
532    /// use prism3_clock::meter::TimeMeter;
533    ///
534    /// let mut meter = TimeMeter::start_now();
535    /// assert!(!meter.is_stopped());
536    /// meter.stop();
537    /// assert!(meter.is_stopped());
538    /// ```
539    pub fn is_stopped(&self) -> bool {
540        self.end_time.is_some()
541    }
542
543    /// Returns a reference to the clock used by this meter.
544    ///
545    /// # Returns
546    ///
547    /// A reference to the clock
548    ///
549    /// # Examples
550    ///
551    /// ```
552    /// use prism3_clock::meter::TimeMeter;
553    ///
554    /// let meter = TimeMeter::new();
555    /// let clock = meter.clock();
556    /// ```
557    pub fn clock(&self) -> &C {
558        &self.clock
559    }
560
561    /// Returns a mutable reference to the clock used by this meter.
562    ///
563    /// # Returns
564    ///
565    /// A mutable reference to the clock
566    ///
567    /// # Examples
568    ///
569    /// ```
570    /// use prism3_clock::meter::TimeMeter;
571    ///
572    /// let mut meter = TimeMeter::new();
573    /// let clock = meter.clock_mut();
574    /// ```
575    pub fn clock_mut(&mut self) -> &mut C {
576        &mut self.clock
577    }
578}
579
580impl TimeMeter<MonotonicClock> {
581    /// Creates a new time meter using the default `MonotonicClock`.
582    ///
583    /// The default clock uses `MonotonicClock`, which is based on
584    /// `Instant` and is not affected by system time adjustments, making
585    /// it more suitable for time measurement.
586    ///
587    /// # Returns
588    ///
589    /// A new `TimeMeter` instance
590    ///
591    /// # Examples
592    ///
593    /// ```
594    /// use prism3_clock::meter::TimeMeter;
595    ///
596    /// let meter = TimeMeter::new();
597    /// ```
598    pub fn new() -> Self {
599        Self::with_clock(MonotonicClock::new())
600    }
601
602    /// Creates a new time meter using the default `MonotonicClock` and
603    /// starts it immediately.
604    ///
605    /// # Returns
606    ///
607    /// A new `TimeMeter` instance that has already been started
608    ///
609    /// # Examples
610    ///
611    /// ```
612    /// use prism3_clock::meter::TimeMeter;
613    ///
614    /// let meter = TimeMeter::start_now();
615    /// ```
616    pub fn start_now() -> Self {
617        Self::with_clock_started(MonotonicClock::new())
618    }
619}
620
621impl Default for TimeMeter<MonotonicClock> {
622    fn default() -> Self {
623        Self::new()
624    }
625}