Files
HealthLog/src/app/api/export
Marc-André Bombeck 96076dbc33 feat(export): emit ISO-8601 with user-tz offset instead of Z
v1.4.25 W7 phase 3.1 — closes the export half of issue #167.

CSV/JSON exports previously emitted timestamps via .toISOString(),
producing 2026-05-15T09:00:00.000Z. Excel and LibreOffice drop the
trailing Z silently and render the bare datetime in the viewer's
locale, so a Warsaw user reads UTC as local time — 11:00 entries
display as 09:00 in their spreadsheet.

Export helpers now accept an optional userTz argument and emit
ISO-8601 with the user's UTC offset
(2026-05-15T11:00:00+02:00). The offset survives the round-trip and
the on-screen value is unambiguous regardless of the spreadsheet's
defaults. Routes resolve the user's stored timezone via the v1.4.25
W7 phase 2 resolver and thread it through formatMeasurements /
formatIntakeEvents / formatMoodEntries.

Backward-compatible: userTz is optional. When omitted (admin tools,
canonical-backup-on-disk shape) the helpers fall back to
.toISOString(), keeping the legacy contract for the JSON full-backup
route that the upload-restore validator depends on.

Surfaces touched:
  - src/lib/export.ts (3 formatters + helper)
  - src/app/api/export/measurements/route.ts
  - src/app/api/export/medications/route.ts (intake history block)
  - src/app/api/export/mood/route.ts
  - src/app/api/export/route.ts (legacy aggregate route)

Tests: 5 new cases in src/lib/__tests__/export.test.ts covering
Berlin / Auckland / New_York offsets, the no-userTz fallback, and
the mood-entry case where the stored YYYY-MM-DD date column stays
unchanged (see proposal §6.3 for the read-side rationale).
2026-05-14 11:55:06 +02:00
..