use std::time::SystemTime;
use anstyle::Style;
use jiff::Timestamp;
#[must_use]
pub fn format_time(time: SystemTime) -> String {
let secs = match time.duration_since(SystemTime::UNIX_EPOCH) {
Ok(d) => i64::try_from(d.as_secs()).unwrap_or(i64::MAX),
Err(e) => i64::try_from(e.duration().as_secs())
.map_or(i64::MIN, |s| s.checked_neg().unwrap_or(i64::MIN)),
};
Timestamp::from_second(secs).map_or_else(
|_| "0000-00-00T00:00:00Z".to_string(),
|ts| ts.strftime("%Y-%m-%dT%H:%M:%SZ").to_string(),
)
}
const HOUR: u64 = 3600;
const DAY: u64 = 24 * HOUR;
const YEAR: u64 = 365 * DAY;
fn parse_iso8601_z(s: &str) -> Option<(&str, &str, &str, &str, &str, &str)> {
let (date_str, after_t) = s.split_once('T')?;
let (year_month, day) = date_str.rsplit_once('-')?;
let (year, month) = year_month.rsplit_once('-')?;
let (hh, after_hh) = after_t.split_once(':')?;
let (mm, ss_with_z) = after_hh.split_once(':')?;
let ss = ss_with_z.strip_suffix('Z')?;
Some((year, month, day, hh, mm, ss))
}
#[must_use]
pub fn format_time_styled(time: SystemTime, now: SystemTime, dim: Style) -> String {
use std::fmt::Write;
let plain = format_time(time);
let Ok(past) = now.duration_since(time) else {
return plain;
};
let past_secs = past.as_secs();
let (year, month, day, hh, mm, ss) =
parse_iso8601_z(&plain).expect("format_time emits YYYY-MM-DDTHH:MM:SSZ");
let segs: [(&str, bool); 12] = [
(year, past_secs < YEAR),
("-", past_secs < YEAR),
(month, past_secs < DAY),
("-", past_secs < DAY),
(day, false),
("T", true),
(hh, past_secs >= DAY),
(":", past_secs >= DAY),
(mm, past_secs >= DAY),
(":", past_secs >= HOUR),
(ss, past_secs >= HOUR),
("Z", true),
];
let mut out = String::with_capacity(plain.len() + 8);
let mut opened = false;
for (text, dim_seg) in segs {
if dim_seg && !opened {
let _ = write!(out, "{dim}");
opened = true;
} else if !dim_seg && opened {
let _ = write!(out, "{}", dim.render_reset());
opened = false;
}
out.push_str(text);
}
if opened {
let _ = write!(out, "{}", dim.render_reset());
}
out
}
#[cfg(test)]
mod tests {
use super::{format_time, format_time_styled, parse_iso8601_z};
use anstyle::{Effects, Style};
use std::time::{Duration, SystemTime};
#[test]
fn parse_iso8601_z_accepts_well_formed_input() {
assert_eq!(
parse_iso8601_z("2026-05-01T09:40:17Z"),
Some(("2026", "05", "01", "09", "40", "17"))
);
}
#[test]
fn parse_iso8601_z_keeps_negative_year_sign() {
assert_eq!(
parse_iso8601_z("-0044-03-15T12:00:00Z"),
Some(("-0044", "03", "15", "12", "00", "00"))
);
}
#[test]
fn parse_iso8601_z_rejects_missing_separators() {
assert_eq!(parse_iso8601_z(""), None); assert_eq!(parse_iso8601_z("2026T09:40:17Z"), None); assert_eq!(parse_iso8601_z("2026-05T09:40:17Z"), None); assert_eq!(parse_iso8601_z("2026-05-01T0940:17Z"), None); assert_eq!(parse_iso8601_z("2026-05-01T09:4017Z"), None); assert_eq!(parse_iso8601_z("2026-05-01T09:40:17"), None); }
fn dim() -> Style {
Style::new().effects(Effects::DIMMED)
}
fn open() -> String {
format!("{}", dim())
}
fn close() -> String {
format!("{}", dim().render_reset())
}
fn strip(s: &str) -> String {
s.replace(&open(), "").replace(&close(), "")
}
const T_2026_05_01: u64 = 1_777_628_417;
#[test]
fn epoch_renders_iso_zulu() {
assert_eq!(format_time(SystemTime::UNIX_EPOCH), "1970-01-01T00:00:00Z");
}
#[test]
fn known_timestamp_round_trips() {
let t = SystemTime::UNIX_EPOCH + Duration::from_secs(T_2026_05_01);
assert_eq!(format_time(t), "2026-05-01T09:40:17Z");
}
#[test]
fn pre_epoch_renders_correctly() {
let t = SystemTime::UNIX_EPOCH - Duration::from_secs(1);
assert_eq!(format_time(t), "1969-12-31T23:59:59Z");
}
#[test]
fn timestamp_out_of_range_renders_zero_string() {
let huge = SystemTime::UNIX_EPOCH + Duration::from_secs(300_000_000_017);
assert_eq!(format_time(huge), "0000-00-00T00:00:00Z");
}
#[test]
fn styled_strips_back_to_plain_format() {
let time = SystemTime::UNIX_EPOCH + Duration::from_secs(T_2026_05_01);
let now = time + Duration::from_secs(30);
let styled = format_time_styled(time, now, dim());
assert_eq!(strip(&styled), format_time(time));
}
#[test]
fn styled_dims_year_and_month_within_24h() {
let time = SystemTime::UNIX_EPOCH + Duration::from_secs(T_2026_05_01);
let now = time + Duration::from_mins(30);
let styled = format_time_styled(time, now, dim());
assert_eq!(
styled,
format!(
"{o}2026-05-{c}01{o}T{c}09:40:17{o}Z{c}",
o = open(),
c = close()
),
);
}
#[test]
fn styled_dims_seconds_once_at_least_an_hour_old() {
let time = SystemTime::UNIX_EPOCH + Duration::from_secs(T_2026_05_01);
let now = time + Duration::from_hours(2);
let styled = format_time_styled(time, now, dim());
assert_eq!(
styled,
format!(
"{o}2026-05-{c}01{o}T{c}09:40{o}:17Z{c}",
o = open(),
c = close()
),
);
}
#[test]
fn styled_dims_only_year_within_a_year() {
let time = SystemTime::UNIX_EPOCH + Duration::from_secs(T_2026_05_01);
let now = time + Duration::from_hours(7 * 24);
let styled = format_time_styled(time, now, dim());
assert_eq!(
styled,
format!("{o}2026-{c}05-01{o}T09:40:17Z{c}", o = open(), c = close()),
);
}
#[test]
fn styled_renders_future_timestamps_fully_bright() {
let time = SystemTime::UNIX_EPOCH + Duration::from_secs(T_2026_05_01);
let now = time - Duration::from_hours(2);
let styled = format_time_styled(time, now, dim());
assert_eq!(styled, "2026-05-01T09:40:17Z");
}
#[test]
fn styled_leaves_date_bright_beyond_a_year() {
let time = SystemTime::UNIX_EPOCH + Duration::from_secs(T_2026_05_01);
let now = time + Duration::from_hours(400 * 24);
let styled = format_time_styled(time, now, dim());
assert_eq!(
styled,
format!("2026-05-01{o}T09:40:17Z{c}", o = open(), c = close()),
);
}
}