The instance-locale milestone exposed default_timezone (IANA name, e.g. Europe/Stockholm)
via GET /api/config + useConfig(). It is deliberately not consumed in the UI yet — there
are currently no human-facing timestamp displays (the only date shown is recording_date, a
plain DATE with no time/zone). Building a formatter now would be dead code (YAGNI).
This issue tracks wiring it up when the first timestamp display lands.
Ask
When the UI starts showing UTC timestamps (e.g. an audit-history view, object created_at/ updated_at, or similar):
Add a small formatTimestamp(utcIso, timeZone, locale) helper using Intl.DateTimeFormat(locale, { timeZone }), reading timeZone from useConfig().default_timezone
and locale from the active i18n language.
Render all UTC timestamps through it (storage/transmission stay UTC — this is display-only).
Guard against an invalid IANA string (Intl throws) — fall back to UTC display rather than crash.
Related
Server-side timestamp formatting (no browser) is owned by the PDF export (#39), which will
need the same default_timezone value plus a Rust tz library.
Notes
The config value already exists; this is purely a display-layer task with no backend change.
## Context
The instance-locale milestone exposed `default_timezone` (IANA name, e.g. `Europe/Stockholm`)
via `GET /api/config` + `useConfig()`. It is deliberately **not consumed in the UI yet** — there
are currently no human-facing timestamp displays (the only date shown is `recording_date`, a
plain `DATE` with no time/zone). Building a formatter now would be dead code (YAGNI).
This issue tracks wiring it up when the first timestamp display lands.
## Ask
When the UI starts showing UTC timestamps (e.g. an audit-history view, object `created_at`/
`updated_at`, or similar):
- Add a small `formatTimestamp(utcIso, timeZone, locale)` helper using
`Intl.DateTimeFormat(locale, { timeZone })`, reading `timeZone` from `useConfig().default_timezone`
and `locale` from the active i18n language.
- Render all UTC timestamps through it (storage/transmission stay UTC — this is display-only).
- Guard against an invalid IANA string (Intl throws) — fall back to UTC display rather than crash.
## Related
- Server-side timestamp formatting (no browser) is owned by the PDF export (#39), which will
need the same `default_timezone` value plus a Rust tz library.
## Notes
- The config value already exists; this is purely a display-layer task with no backend change.
Added a shared formatTimestamp(value, timeZone, locale) helper (web/src/lib/format-timestamp.ts) that renders a UTC ISO timestamp as date + short time in the instance timezone + active locale, via Intl.DateTimeFormat. The objects-table "Updated" column now routes through it — previously it was an inline, date-only, unguarded Intl.DateTimeFormat that would have thrown a RangeError and crashed the table on a misconfigured default_timezone.
Key points:
Invalid-IANA guard: a bad timeZone falls back to UTC formatting (try/catch) rather than crashing.
Edge handling mirrors the sibling lib/format-date.ts: null → "—", non-string → String(value), unparseable → returned unchanged.
Display-only — storage/transmission stay UTC. No backend change, no new dependency, no new i18n keys.
4 unit tests incl. the load-bearing timezone day-shift (02:00 UTC = prev-day in America/New_York) and the invalid-zone no-throw case.
Future timestamp displays (object-detail created/updated, audit history, when they land) route through the same helper.
Done — merged to `main` (fe44803).
Added a shared `formatTimestamp(value, timeZone, locale)` helper (`web/src/lib/format-timestamp.ts`) that renders a UTC ISO timestamp as **date + short time** in the instance timezone + active locale, via `Intl.DateTimeFormat`. The objects-table "Updated" column now routes through it — previously it was an inline, date-only, unguarded `Intl.DateTimeFormat` that would have thrown a `RangeError` and crashed the table on a misconfigured `default_timezone`.
Key points:
- **Invalid-IANA guard:** a bad `timeZone` falls back to UTC formatting (try/catch) rather than crashing.
- Edge handling mirrors the sibling `lib/format-date.ts`: `null` → "—", non-string → `String(value)`, unparseable → returned unchanged.
- **Display-only** — storage/transmission stay UTC. No backend change, no new dependency, no new i18n keys.
- 4 unit tests incl. the load-bearing timezone day-shift (02:00 UTC = prev-day in `America/New_York`) and the invalid-zone no-throw case.
Gate green: 293 tests pass, largest chunk 90.2 KB gz (budget 250), `check:colors` clean.
Future timestamp displays (object-detail created/updated, audit history, when they land) route through the same helper.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Context
The instance-locale milestone exposed
default_timezone(IANA name, e.g.Europe/Stockholm)via
GET /api/config+useConfig(). It is deliberately not consumed in the UI yet — thereare currently no human-facing timestamp displays (the only date shown is
recording_date, aplain
DATEwith no time/zone). Building a formatter now would be dead code (YAGNI).This issue tracks wiring it up when the first timestamp display lands.
Ask
When the UI starts showing UTC timestamps (e.g. an audit-history view, object
created_at/updated_at, or similar):formatTimestamp(utcIso, timeZone, locale)helper usingIntl.DateTimeFormat(locale, { timeZone }), readingtimeZonefromuseConfig().default_timezoneand
localefrom the active i18n language.Related
need the same
default_timezonevalue plus a Rust tz library.Notes
Done — merged to
main(fe44803).Added a shared
formatTimestamp(value, timeZone, locale)helper (web/src/lib/format-timestamp.ts) that renders a UTC ISO timestamp as date + short time in the instance timezone + active locale, viaIntl.DateTimeFormat. The objects-table "Updated" column now routes through it — previously it was an inline, date-only, unguardedIntl.DateTimeFormatthat would have thrown aRangeErrorand crashed the table on a misconfigureddefault_timezone.Key points:
timeZonefalls back to UTC formatting (try/catch) rather than crashing.lib/format-date.ts:null→ "—", non-string →String(value), unparseable → returned unchanged.America/New_York) and the invalid-zone no-throw case.Gate green: 293 tests pass, largest chunk 90.2 KB gz (budget 250),
check:colorsclean.Future timestamp displays (object-detail created/updated, audit history, when they land) route through the same helper.