import { locale, t } from '@/i18n' const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] const MINUTE_MS = 60 * 1000 const HOUR_MS = 60 * MINUTE_MS const ISO_DAY = /^(\d{4})-(\d{2})-(\d{2})$/ export interface DayGroup { key: string label: string items: T[] } function pad(value: number) { return String(value).padStart(2, '0') } function monthShort(date: Date, timeZone?: 'UTC') { if (locale.value === 'fr') { return new Intl.DateTimeFormat('fr', { month: 'short', timeZone, }).format(date) } return MONTHS[timeZone ? date.getUTCMonth() : date.getMonth()] } export function formatAbsoluteDay(date: Date, timeZone?: 'UTC') { const day = timeZone ? date.getUTCDate() : date.getDate() const year = timeZone ? date.getUTCFullYear() : date.getFullYear() return `${day} ${monthShort(date, timeZone)} ${year}` } function clock(date: Date, withSeconds: boolean, utc: boolean) { const hours = utc ? date.getUTCHours() : date.getHours() const minutes = utc ? date.getUTCMinutes() : date.getMinutes() const seconds = utc ? date.getUTCSeconds() : date.getSeconds() const base = `${pad(hours)}:${pad(minutes)}` return withSeconds ? `${base}:${pad(seconds)}` : base } export function localDayKey(date: Date) { return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}` } function startOfDay(date: Date) { return new Date(date.getFullYear(), date.getMonth(), date.getDate()) } export function dayLabel(iso: string, now: Date = new Date()) { const date = new Date(iso) const today = startOfDay(now) const yesterday = new Date(today.getFullYear(), today.getMonth(), today.getDate() - 1) const key = localDayKey(date) if (key === localDayKey(today)) return t('Today') if (key === localDayKey(yesterday)) return t('Yesterday') return formatAbsoluteDay(date) } export function whenLabel(iso: string, now: Date = new Date()) { const date = new Date(iso) if (localDayKey(date) !== localDayKey(now)) return clock(date, false, false) const elapsed = Math.max(0, now.getTime() - date.getTime()) if (elapsed < HOUR_MS) { return t('{0} min ago', [Math.max(1, Math.floor(elapsed / MINUTE_MS))]) } return t('{0} h ago', [Math.floor(elapsed / HOUR_MS)]) } export function recentWhenLabel(iso: string, now: Date = new Date()) { const date = new Date(iso) const key = localDayKey(date) if (key === localDayKey(now)) return whenLabel(iso, now) const today = startOfDay(now) const yesterday = new Date(today.getFullYear(), today.getMonth(), today.getDate() - 1) if (key === localDayKey(yesterday)) return `${t('Yesterday')}, ${clock(date, false, false)}` return localDayTime(iso) } export function localDayTime(iso: string) { const date = new Date(iso) return `${formatAbsoluteDay(date)}, ${clock(date, false, false)}` } export function fullLocalTime(iso: string) { const date = new Date(iso) return `${formatAbsoluteDay(date)}, ${clock(date, true, false)}` } export function fullUtcTime(iso: string) { const date = new Date(iso) return `${formatAbsoluteDay(date, 'UTC')}, ${clock(date, true, true)}` } export function groupByDay( items: T[], now: Date = new Date(), ): DayGroup[] { const groups: DayGroup[] = [] for (const item of items) { const key = localDayKey(new Date(item.created_at)) const last = groups[groups.length - 1] if (last && last.key === key) { last.items.push(item) continue } groups.push({ key, label: dayLabel(item.created_at, now), items: [item], }) } return groups } function parseIsoDay(value: string) { const match = ISO_DAY.exec(value) if (!match) return null return new Date(Number(match[1]), Number(match[2]) - 1, Number(match[3]), 12) } export function formatDateRange(from: string, to: string) { const start = parseIsoDay(from) const end = parseIsoDay(to) if (!start || !end) return '' const sameYear = start.getFullYear() === end.getFullYear() const sameMonth = sameYear && start.getMonth() === end.getMonth() if (sameMonth && start.getDate() === end.getDate()) return formatAbsoluteDay(end) if (sameMonth) return `${start.getDate()} – ${formatAbsoluteDay(end)}` if (sameYear) return `${start.getDate()} ${monthShort(start)} – ${formatAbsoluteDay(end)}` return `${formatAbsoluteDay(start)} – ${formatAbsoluteDay(end)}` }