const dateTimeFmt = new Intl.DateTimeFormat(undefined, { dateStyle: 'medium', timeStyle: 'short' }); const timeFmt = new Intl.DateTimeFormat(undefined, { timeStyle: 'medium' }); const relFmt = new Intl.RelativeTimeFormat(undefined, { numeric: 'auto' }); const REL_UNITS = [ ['year', 31_536_000], ['month', 2_592_000], ['week', 604_800], ['day', 86_400], ['hour', 3_600], ['minute', 60], ]; export function parseDate(value) { if (!value) return null; const d = new Date(value); return Number.isNaN(d.getTime()) ? null : d; } export const formatDateTime = (d) => (d ? dateTimeFmt.format(d) : '—'); export const formatTime = (d) => timeFmt.format(d); export function formatRelative(d, now = Date.now()) { if (!d) return ''; const diffSec = Math.round((d.getTime() - now) / 1000); for (const [unit, secs] of REL_UNITS) { if (Math.abs(diffSec) >= secs) return relFmt.format(Math.round(diffSec / secs), unit); } return relFmt.format(diffSec, 'second'); } export function formatDuration(totalSec) { if (totalSec == null || Number.isNaN(Number(totalSec))) return null; const s = Math.max(0, Math.round(totalSec)); const h = Math.floor(s / 3600); const m = Math.floor((s % 3600) / 60); const sec = s % 60; return [h && `${h}h`, (h || m) && `${m}m`, `${sec}s`].filter(Boolean).join(' '); } export function formatBytes(bytes) { if (bytes == null || Number.isNaN(Number(bytes))) return null; const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']; let n = Number(bytes); let i = 0; while (n >= 1024 && i < units.length - 1) { n /= 1024; i++; } return `${n.toFixed(i === 0 ? 0 : 1)} ${units[i]}`; }