Skip to main content

zer_adapters/
time.rs

1/// Shared time utilities for benchmark output.
2///
3/// Avoids pulling in `chrono` while still producing sortable ISO-8601 timestamps.
4use std::time::{SystemTime, UNIX_EPOCH};
5
6/// Returns the current UTC time as an ISO-8601 string: `YYYY-MM-DDTHH:MM:SSZ`.
7pub fn utc_timestamp_iso() -> String {
8    fmt_unix_secs(unix_secs_now())
9}
10
11/// Returns the current UNIX timestamp in seconds.
12pub fn unix_secs_now() -> u64 {
13    SystemTime::now()
14        .duration_since(UNIX_EPOCH)
15        .unwrap_or_default()
16        .as_secs()
17}
18
19/// Format a UNIX timestamp (seconds since epoch) as `YYYY-MM-DDTHH:MM:SSZ`.
20pub fn fmt_unix_secs(s: u64) -> String {
21    let sec = s % 60;
22    let min = (s / 60) % 60;
23    let hour = (s / 3600) % 24;
24    let days = s / 86400;
25    let year_400 = days / 146097;
26    let rem = days % 146097;
27    let year_100 = rem / 36524;
28    let rem = rem % 36524;
29    let year_4 = rem / 1461;
30    let rem = rem % 1461;
31    let year_1 = rem / 365;
32    let year = 1970 + year_400 * 400 + year_100 * 100 + year_4 * 4 + year_1;
33    let doy = rem % 365;
34    let (month, day) = doy_to_md(doy, is_leap(year));
35    format!("{year:04}-{month:02}-{day:02}T{hour:02}:{min:02}:{sec:02}Z")
36}
37
38fn is_leap(y: u64) -> bool {
39    (y.is_multiple_of(4) && !y.is_multiple_of(100)) || y.is_multiple_of(400)
40}
41
42fn doy_to_md(doy: u64, leap: bool) -> (u64, u64) {
43    let days_in_month: [u64; 12] = if leap {
44        [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
45    } else {
46        [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
47    };
48    let mut rem = doy;
49    for (i, &dim) in days_in_month.iter().enumerate() {
50        if rem < dim {
51            return (i as u64 + 1, rem + 1);
52        }
53        rem -= dim;
54    }
55    (12, 31)
56}
57
58#[cfg(test)]
59mod tests {
60    use super::*;
61
62    #[test]
63    fn epoch_formats_correctly() {
64        assert_eq!(fmt_unix_secs(0), "1970-01-01T00:00:00Z");
65    }
66
67    #[test]
68    fn known_date_formats_correctly() {
69        // 2024-01-01T00:00:00Z = 1704067200
70        assert_eq!(fmt_unix_secs(1704067200), "2024-01-01T00:00:00Z");
71    }
72
73    #[test]
74    fn unix_secs_now_is_reasonable() {
75        let t = unix_secs_now();
76        assert!(t > 1_700_000_000, "timestamp must be after 2023");
77    }
78
79    #[test]
80    fn utc_timestamp_iso_parses() {
81        let s = utc_timestamp_iso();
82        assert_eq!(s.len(), 20);
83        assert!(s.ends_with('Z'));
84    }
85}