const MINUTE_MS = 60_000; const HOUR_MS = 60 * MINUTE_MS; const DAY_MS = 24 * HOUR_MS; const MONTH_MS = 30 * DAY_MS; const YEAR_MS = 365 * DAY_MS; export function formatShortRelativeTime(timestamp: number, now: number): string { const elapsed = Math.max(0, now - timestamp); if (elapsed < MINUTE_MS) return 'just now'; if (elapsed < HOUR_MS) return `${Math.floor(elapsed / MINUTE_MS)}m ago`; if (elapsed < DAY_MS) return `${Math.floor(elapsed / HOUR_MS)}h ago`; if (elapsed < MONTH_MS) return `${Math.floor(elapsed / DAY_MS)}d ago`; if (elapsed < YEAR_MS) return `${Math.floor(elapsed / MONTH_MS)}mo ago`; return `${Math.floor(elapsed / YEAR_MS)}y ago`; } export function formatExactTime(timestamp: number): string { return new Intl.DateTimeFormat(undefined, { dateStyle: 'medium', timeStyle: 'short' }).format(timestamp); } /** Time of day only. Paired with a day divider so history is not full of dates. */ export function formatTimeOfDay(timestamp: number): string { return new Intl.DateTimeFormat(undefined, { timeStyle: 'short' }).format(timestamp); } export function isSameDay(left: number, right: number): boolean { const a = new Date(left); const b = new Date(right); return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate(); } /** "Today" / "Yesterday" / an explicit date, for message-history day dividers. */ export function formatDayLabel(timestamp: number, now: number): string { if (isSameDay(timestamp, now)) return 'Today'; if (isSameDay(timestamp, now - DAY_MS)) return 'Yesterday'; return new Intl.DateTimeFormat(undefined, { dateStyle: 'full' }).format(timestamp); } export const STALE_REPLY_THRESHOLD_MS = 12 * HOUR_MS; export const CRITICAL_REPLY_THRESHOLD_MS = 48 * HOUR_MS; export type ConversationAge = 'fresh' | 'stale' | 'critical'; /** * How overdue an unanswered conversation is. Only meaningful for conversations * waiting on us — "3d ago" on a conversation we already replied to is fine, but * on one awaiting a reply it is the thing the operator most needs to notice. */ export function conversationAge(timestamp: number, now: number): ConversationAge { const elapsed = Math.max(0, now - timestamp); if (elapsed >= CRITICAL_REPLY_THRESHOLD_MS) return 'critical'; if (elapsed >= STALE_REPLY_THRESHOLD_MS) return 'stale'; return 'fresh'; }