fix(ui): render absolute timestamps in the viewer's local timezone

Download event times showed raw UTC wall-clock (iso.slice). Added
formatDateTime()/formatLocalDate() (local tz, robust to naive vs tz-aware
ISO) and applied them to the download row + detail modal datetimes and the
credential/artist date displays. formatPostDate stays UTC (date-only,
locale-stable, unit-tested).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-28 19:36:42 -04:00
parent 8c3900b998
commit c87e8e0932
6 changed files with 40 additions and 9 deletions
+29
View File
@@ -32,3 +32,32 @@ export function formatRelative(iso, opts = {}) {
if (future) return diff <= 0 ? 'imminent' : `in ${body}`
return `${body} ago`
}
// Parse a backend timestamp as a UTC instant. Backend serializes tz-aware
// UTC (…+00:00); if a value ever arrives without a tz designator we treat
// it as UTC so it still converts to the viewer's zone correctly.
function _parseUtc (iso) {
if (typeof iso !== 'string' || !iso) return null
const hasTz = /([zZ])$|([+-]\d{2}:?\d{2})$/.test(iso)
const d = new Date(hasTz ? iso : `${iso}Z`)
return isNaN(d.getTime()) ? null : d
}
// Local-timezone date + time, e.g. "May 28, 7:30 PM". Use for absolute
// timestamps that were previously shown as raw UTC ISO.
export function formatDateTime (iso) {
const d = _parseUtc(iso)
if (!d) return ''
return d.toLocaleString(undefined, {
month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit',
})
}
// Local-timezone date only, e.g. "May 28, 2026".
export function formatLocalDate (iso) {
const d = _parseUtc(iso)
if (!d) return ''
return d.toLocaleDateString(undefined, {
year: 'numeric', month: 'short', day: 'numeric',
})
}