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