export function formatBytes(n: number): string { const num = Number(n) || 0; if (num < 1024) return `${num} B`; const units = ['KiB', 'MiB', 'GiB', 'TiB', 'PiB']; let v = num / 1024; let i = 0; while (v >= 1024 && i < units.length - 1) { v /= 1024; i++; } return `${v.toFixed(2)} ${units[i]}`; } function pad(n: number): string { return n < 10 ? `0${n}` : `${n}`; } // Deterministic, timezone-independent date+time formatter. // Uses UTC getters so the server (UTC) and client (local TZ) render // byte-identical text, avoiding React hydration mismatches (error #425). // Output format: "YYYY-MM-DD HH:MM UTC". export function formatDateTime(s: string | null | undefined): string { if (!s) return '—'; const d = new Date(s); if (Number.isNaN(d.getTime())) return '—'; const date = `${d.getUTCFullYear()}-${pad(d.getUTCMonth() + 1)}-${pad( d.getUTCDate(), )}`; const time = `${pad(d.getUTCHours())}:${pad(d.getUTCMinutes())}`; return `${date} ${time} UTC`; } // Date-only deterministic UTC formatter. Output format: "YYYY-MM-DD UTC". export function formatDateOnly(s: string | null | undefined): string { if (!s) return '—'; const d = new Date(s); if (Number.isNaN(d.getTime())) return '—'; return `${d.getUTCFullYear()}-${pad(d.getUTCMonth() + 1)}-${pad( d.getUTCDate(), )} UTC`; } // Backwards-compatible alias: existing call sites render date+time. export const formatDate = formatDateTime;