//! Human time strings from ISO 8601 UTC timestamps. //! //! Used by the pretty renderer for "resets in 3h12m" and "in 47m". Parses are //! lenient. A parse failure returns the raw string unchanged. use chrono::{DateTime, Utc}; /// Relative phrasing of a future or past timestamp. Example: "in 3h12m" or /// "47m ago". Falls back to the input on parse failure. pub fn relative(iso: &str) -> String { let Some(t) = parse(iso) else { return iso.to_string(); }; let now = Utc::now(); let d = t.signed_duration_since(now); let secs = d.num_seconds(); if secs.abs() < 60 { if secs >= 0 { "in <1m".to_string() } else { "<1m ago".to_string() } } else { let h = secs / 3600; let m = (secs % 3600) / 60; let core = match (h.abs() > 0, m != 0) { (true, true) => format!("{}h{}m", h.abs(), m.abs()), (true, false) => format!("{}h", h.abs()), (false, _) => format!("{}m", m.abs()), }; if secs >= 0 { format!("in {core}") } else { format!("{core} ago") } } } /// Absolute UTC string like "2025-08-31 17:00 UTC". pub fn absolute(iso: &str) -> String { let Some(t) = parse(iso) else { return iso.to_string(); }; t.format("%Y-%m-%d %H:%M UTC").to_string() } fn parse(iso: &str) -> Option> { DateTime::parse_from_rfc3339(iso) .ok() .map(|dt| dt.with_timezone(&Utc)) }