File size: 2,372 Bytes
e59d91d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
/** Parse ISO / Postgres timestamptz strings from APIs. */
export function parseApiDateTime(value: string | null | undefined): Date | null {
  if (!value?.trim() || value === '—') return null
  const d = new Date(value.trim())
  return Number.isNaN(d.getTime()) ? null : d
}

const absoluteFormatter = new Intl.DateTimeFormat(undefined, {
  month: 'short',
  day: 'numeric',
  year: 'numeric',
  hour: 'numeric',
  minute: '2-digit',
})

/** Local calendar + clock, e.g. "May 25, 2026, 8:55 PM". */
export function formatDateTimeAbsolute(value: string | null | undefined): string {
  const d = parseApiDateTime(value)
  if (!d) return value?.trim() || '—'
  return absoluteFormatter.format(d)
}

function formatRelativeToNow(date: Date): string {
  const diffMs = date.getTime() - Date.now()
  const rtf = new Intl.RelativeTimeFormat(undefined, { numeric: 'auto' })
  const absSec = Math.abs(Math.round(diffMs / 1000))
  if (absSec < 45) return rtf.format(Math.round(diffMs / 1000), 'second')
  if (absSec < 45 * 60) return rtf.format(Math.round(diffMs / 60_000), 'minute')
  if (absSec < 22 * 3_600) return rtf.format(Math.round(diffMs / 3_600_000), 'hour')
  if (absSec < 6 * 86_400) return rtf.format(Math.round(diffMs / 86_400_000), 'day')
  return absoluteFormatter.format(date)
}

/** Compact age for live dashboards, e.g. ``5m ago``, ``2h ago``. */
export function formatAgoCompact(
  value: string | null | undefined,
  nowMs = Date.now(),
): string {
  const d = parseApiDateTime(value)
  if (!d) return '—'
  const sec = Math.max(0, Math.floor((nowMs - d.getTime()) / 1000))
  if (sec < 60) return `${Math.max(1, sec)}s ago`
  const m = Math.floor(sec / 60)
  if (m < 60) return `${m}m ago`
  const h = Math.floor(m / 60)
  if (h < 48) return `${h}h ago`
  const days = Math.floor(h / 24)
  return `${days}d ago`
}

/** "Updated" line: relative when within a week, otherwise absolute. */
export function formatUpdatedAt(value: string | null | undefined): {
  display: string
  title: string
} {
  const d = parseApiDateTime(value)
  if (!d) {
    const fallback = value?.trim() || '—'
    return { display: fallback, title: fallback }
  }
  const absolute = formatDateTimeAbsolute(value)
  const ageDays = Math.abs(d.getTime() - Date.now()) / 86_400_000
  return {
    display: ageDays < 7 ? formatRelativeToNow(d) : absolute,
    title: absolute,
  }
}