//! Minimal UTC timestamp/duration formatting, without a `chrono`/`time` //! dependency. Timestamps are stored as Unix seconds (`i64`) everywhere in //! profile/instance state; this module only turns them into text for //! `status`/`list` output. use std::time::{SystemTime, UNIX_EPOCH}; pub fn now() -> i64 { SystemTime::now().duration_since(UNIX_EPOCH).map(|d| d.as_secs() as i64).unwrap_or(0) } /// `YYYY-MM-DD HH:MM:SS UTC`, via Howard Hinnant's civil-from-days algorithm /// (public domain, http://howardhinnant.github.io/date_algorithms.html) - /// avoids pulling in a whole calendar/timezone crate for what's otherwise a /// handful of integer operations. pub fn fmt_timestamp(unix_secs: i64) -> String { let days = unix_secs.div_euclid(86_400); let secs_of_day = unix_secs.rem_euclid(86_400); let (h, m, s) = (secs_of_day / 3600, (secs_of_day / 60) % 60, secs_of_day % 60); let z = days + 719_468; let era = if z >= 0 { z } else { z - 146_096 } / 146_097; let doe = (z - era * 146_097) as i64; // [0, 146096] let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365; // [0, 399] let y = yoe + era * 400; let doy = doe - (365 * yoe + yoe / 4 - yoe / 100); // [0, 365] let mp = (5 * doy + 2) / 153; // [0, 11] let d = doy - (153 * mp + 2) / 5 + 1; // [1, 31] let m_num = if mp < 10 { mp + 3 } else { mp - 9 }; // [1, 12] let y = if m_num <= 2 { y + 1 } else { y }; format!("{y:04}-{m_num:02}-{d:02} {h:02}:{m:02}:{s:02} UTC") } /// Compact `1d 02h 03m 04s`-style duration, dropping leading zero units. pub fn fmt_duration(secs: i64) -> String { let secs = secs.max(0); let (d, rem) = (secs / 86_400, secs % 86_400); let (h, rem) = (rem / 3600, rem % 3600); let (m, s) = (rem / 60, rem % 60); match (d, h, m) { (d, _, _) if d > 0 => format!("{d}d {h:02}h {m:02}m {s:02}s"), (_, h, _) if h > 0 => format!("{h}h {m:02}m {s:02}s"), (_, _, m) if m > 0 => format!("{m}m {s:02}s"), _ => format!("{s}s"), } } #[cfg(test)] mod tests { use super::*; #[test] fn formats_known_epoch() { assert_eq!(fmt_timestamp(0), "1970-01-01 00:00:00 UTC"); assert_eq!(fmt_timestamp(1_700_000_000), "2023-11-14 22:13:20 UTC"); } #[test] fn formats_durations() { assert_eq!(fmt_duration(5), "5s"); assert_eq!(fmt_duration(65), "1m 05s"); assert_eq!(fmt_duration(3665), "1h 01m 05s"); assert_eq!(fmt_duration(90_065), "1d 01h 01m 05s"); } }