Skip to main content

dynomite/util/
time.rs

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