//! Times as people read them: relative ("3 hours ago") with the absolute //! form for a tooltip. use time::format_description::well_known::Rfc3339; use time::OffsetDateTime; /// `just now`, `5 minutes ago`, `3 days ago`, `2 years ago`. pub fn ago(when_ms: u64, now_ms: u64) -> String { if when_ms == 0 { return "unknown".into(); } if when_ms > now_ms { return "in the future".into(); } let s = (now_ms - when_ms) / 1000; let (n, unit) = if s < 45 { return "just now".into(); } else if s < 90 { (1, "minute") } else if s < 3600 { (s / 60, "minute") } else if s < 86_400 { (s / 3600, "hour") } else if s < 86_400 * 30 { (s / 86_400, "day") } else if s < 86_400 * 365 { (s / (86_400 * 30), "month") } else { (s / (86_400 * 365), "year") }; format!("{n} {unit}{} ago", if n == 1 { "" } else { "s" }) } /// `2026-09-27 17:02 UTC`. pub fn absolute(when_ms: u64) -> String { match OffsetDateTime::from_unix_timestamp((when_ms / 1000) as i64) { Ok(t) => { let f = time::macros::format_description!("[year]-[month]-[day] [hour]:[minute] UTC"); t.format(&f).unwrap_or_default() } Err(_) => String::new(), } } /// `21 Oct 2026`, the UTC day. pub fn day(when_ms: u64) -> String { match OffsetDateTime::from_unix_timestamp((when_ms / 1000) as i64) { Ok(t) => { let f = time::macros::format_description!("[day padding:none] [month repr:short] [year]"); t.format(&f).unwrap_or_default() } Err(_) => String::new(), } } pub fn rfc3339(when_ms: u64) -> String { OffsetDateTime::from_unix_timestamp((when_ms / 1000) as i64).ok().and_then(|t| t.format(&Rfc3339).ok()).unwrap_or_default() } #[cfg(test)] mod tests { use super::*; #[test] fn days() { assert_eq!(day(1_792_540_800_000), "21 Oct 2026"); } #[test] fn relative() { let now = 1_800_000_000_000; assert_eq!(ago(now - 10_000, now), "just now"); assert_eq!(ago(now - 60_000, now), "1 minute ago"); assert_eq!(ago(now - 5 * 60_000, now), "5 minutes ago"); assert_eq!(ago(now - 3 * 3_600_000, now), "3 hours ago"); assert_eq!(ago(now - 2 * 86_400_000, now), "2 days ago"); assert_eq!(ago(now - 400 * 86_400_000, now), "1 year ago"); assert_eq!(ago(0, now), "unknown"); assert_eq!(absolute(1_758_990_120_000), "2025-09-27 16:22 UTC"); } }