ocd/src/timefmt.rs

52 lines
1.5 KiB
Rust
Raw Normal View History

//! 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<Utc>> {
DateTime::parse_from_rfc3339(iso)
.ok()
.map(|dt| dt.with_timezone(&Utc))
}