/** Formatting helpers for dates, sizes and durations. */ const MONTHS = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December', ]; export function formatBytes(bytes?: number): string { if (!bytes || bytes <= 0) return '—'; const units = ['B', 'KB', 'MB', 'GB', 'TB']; const i = Math.min(units.length - 1, Math.floor(Math.log(bytes) / Math.log(1024))); const value = bytes / Math.pow(1024, i); return `${value.toFixed(value >= 10 || i === 0 ? 0 : 1)} ${units[i]}`; } export function formatDuration(seconds?: number): string { if (!seconds || seconds < 0) return ''; const s = Math.floor(seconds % 60); const m = Math.floor((seconds / 60) % 60); const h = Math.floor(seconds / 3600); const pad = (n: number) => n.toString().padStart(2, '0'); return h > 0 ? `${h}:${pad(m)}:${pad(s)}` : `${m}:${pad(s)}`; } export function formatDate(ms: number): string { const d = new Date(ms); return `${MONTHS[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()}`; } export function formatDay(ms: number): string { const d = new Date(ms); const today = new Date(); const yesterday = new Date(today); yesterday.setDate(today.getDate() - 1); const sameDay = (a: Date, b: Date) => a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate(); if (sameDay(d, today)) return 'Today'; if (sameDay(d, yesterday)) return 'Yesterday'; const weekday = d.toLocaleDateString(undefined, { weekday: 'long' }); return `${weekday}, ${MONTHS[d.getMonth()]} ${d.getDate()}`; } export function formatMonth(ms: number): string { const d = new Date(ms); return `${MONTHS[d.getMonth()]} ${d.getFullYear()}`; } export function formatYear(ms: number): string { return new Date(ms).getFullYear().toString(); } export function formatTime(ms: number): string { return new Date(ms).toLocaleTimeString(undefined, { hour: 'numeric', minute: '2-digit' }); } export function relativeTime(ms: number): string { const diff = Date.now() - ms; const day = 24 * 60 * 60 * 1000; if (diff < 0) return 'in the future'; if (diff < day) return 'today'; const days = Math.floor(diff / day); if (days === 1) return 'yesterday'; if (days < 30) return `${days} days ago`; const months = Math.floor(days / 30); if (months < 12) return `${months} month${months > 1 ? 's' : ''} ago`; const years = Math.floor(days / 365); return `${years} year${years > 1 ? 's' : ''} ago`; } /** Days remaining before a trashed item is permanently removed. */ export function daysUntilPermanentDelete(deletedAt: number, retentionMs: number): number { const remaining = deletedAt + retentionMs - Date.now(); return Math.max(0, Math.ceil(remaining / (24 * 60 * 60 * 1000))); }