// Time formatting helpers. Relative for at-a-glance scanning, absolute (mono) // for the precise value on hover. const UNITS: [Intl.RelativeTimeFormatUnit, number][] = [ ["year", 60 * 60 * 24 * 365], ["month", 60 * 60 * 24 * 30], ["day", 60 * 60 * 24], ["hour", 60 * 60], ["minute", 60], ["second", 1], ]; const rtf = new Intl.RelativeTimeFormat(undefined, { numeric: "auto" }); /** * Human relative time, e.g. "3 minutes ago" / "in 2 days". Returns "—" for * missing input and "just now" for sub-10-second deltas. */ export function relativeTime(iso: string | null | undefined): string { if (!iso) return "—"; const then = new Date(iso).getTime(); if (Number.isNaN(then)) return "—"; const deltaSec = Math.round((then - Date.now()) / 1000); const abs = Math.abs(deltaSec); if (abs < 10) return "just now"; for (const [unit, secs] of UNITS) { if (abs >= secs || unit === "second") { return rtf.format(Math.round(deltaSec / secs), unit); } } return "just now"; } /** Absolute local timestamp for tooltips / mono display. */ export function absoluteTime(iso: string | null | undefined): string { if (!iso) return "—"; const d = new Date(iso); if (Number.isNaN(d.getTime())) return "—"; return d.toLocaleString(undefined, { year: "numeric", month: "short", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit", }); } /** Format a duration in seconds as a compact "1h 04m" / "47s" string. */ export function formatDuration(secs: number | null | undefined): string { if (secs == null || secs < 0) return "—"; const h = Math.floor(secs / 3600); const m = Math.floor((secs % 3600) / 60); const s = secs % 60; if (h > 0) return `${h}h ${String(m).padStart(2, "0")}m`; if (m > 0) return `${m}m ${String(s).padStart(2, "0")}s`; return `${s}s`; }