export function timeAgo(iso: string | number | Date): string { const d = typeof iso === "string" || typeof iso === "number" ? new Date(iso) : iso; const sec = Math.round((Date.now() - d.getTime()) / 1000); if (!isFinite(sec) || sec < 0) return ""; if (sec < 45) return "just now"; const min = Math.round(sec / 60); if (min < 60) return `${min}m ago`; const hr = Math.round(min / 60); if (hr < 24) return `${hr}h ago`; const day = Math.round(hr / 24); if (day < 30) return `${day}d ago`; const mo = Math.round(day / 30); if (mo < 12) return `${mo}mo ago`; const yr = Math.round(mo / 12); return `${yr}y ago`; } // Format a duration in hours as "H.Hh" under a day, otherwise "Dd Hh". export function formatHours(hours: number): string { if (!isFinite(hours) || hours <= 0) return "0h"; if (hours < 1) { const m = Math.round(hours * 60); return `${m}m`; } if (hours < 24) return `${hours.toFixed(1)}h`; const days = Math.floor(hours / 24); const rem = Math.round(hours - days * 24); return rem ? `${days}d ${rem}h` : `${days}d`; } export function formatBytes(n: number): string { if (!isFinite(n) || n <= 0) return "0 B"; const units = ["B", "KB", "MB", "GB", "TB"]; const i = Math.min(Math.floor(Math.log(n) / Math.log(1024)), units.length - 1); return `${(n / Math.pow(1024, i)).toFixed(i === 0 ? 0 : 1)} ${units[i]}`; }