Skip to main content

dynomite/util/
time.rs

1//! Wall-clock and digit-counting helpers.
2//!
3//! Millisecond / microsecond timestamps and a decimal digit counter,
4//! gathered as plain functions over `std::time::SystemTime`.
5
6use std::time::{SystemTime, UNIX_EPOCH};
7
8use crate::core::types::{Msec, Usec};
9
10/// Microseconds since the UNIX epoch.
11///
12/// Returns zero if the system clock reports a time before the epoch.
13///
14/// # Examples
15///
16/// ```
17/// use dynomite::util::time::usec_now;
18/// assert!(usec_now() > 0);
19/// ```
20pub fn usec_now() -> Usec {
21    SystemTime::now().duration_since(UNIX_EPOCH).map_or(0, |d| {
22        let micros = d.as_micros();
23        if micros > u128::from(Usec::MAX) {
24            Usec::MAX
25        } else {
26            #[allow(clippy::cast_possible_truncation)]
27            {
28                micros as Usec
29            }
30        }
31    })
32}
33
34/// Milliseconds since the UNIX epoch.
35///
36/// # Examples
37///
38/// ```
39/// use dynomite::util::time::msec_now;
40/// let a = msec_now();
41/// let b = msec_now();
42/// assert!(b >= a);
43/// ```
44pub fn msec_now() -> Msec {
45    usec_now() / 1000
46}
47
48/// Number of decimal digits in `arg`, including a leading zero.
49///
50/// # Examples
51///
52/// ```
53/// use dynomite::util::time::count_digits;
54/// assert_eq!(count_digits(0), 1);
55/// assert_eq!(count_digits(9), 1);
56/// assert_eq!(count_digits(10), 2);
57/// assert_eq!(count_digits(u64::MAX), 20);
58/// ```
59pub fn count_digits(arg: u64) -> u32 {
60    if arg == 0 {
61        1
62    } else {
63        arg.ilog10() + 1
64    }
65}
66
67#[cfg(test)]
68mod tests {
69    use super::*;
70
71    #[test]
72    fn msec_is_monotone_non_decreasing() {
73        let a = msec_now();
74        for _ in 0..16 {
75            let b = msec_now();
76            assert!(b >= a);
77        }
78    }
79
80    #[test]
81    fn msec_is_within_usec_to_msec_factor() {
82        let m = msec_now();
83        let u = usec_now();
84        // The two readings happen back-to-back; allow a few ms of skew.
85        assert!(u / 1000 >= m);
86        assert!(u / 1000 <= m + 50);
87    }
88
89    #[test]
90    fn digit_table_matches_ten_powers() {
91        for d in 1u32..=18 {
92            let n = 10u64.pow(d);
93            assert_eq!(count_digits(n), d + 1);
94            assert_eq!(count_digits(n - 1), d);
95        }
96    }
97}