mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 20:42:43 +00:00
feat(format): makeFormatters accepts user timezone override
v1.4.25 W7 phase 3.2 — extends the shared locale formatter to accept an optional second argument carrying the per-user display timezone. makeFormatters(locale) — unchanged, still renders in Europe/Berlin. makeFormatters(locale, userTz) — renders in the user's zone. The empty-string and undefined cases both fall back to DISPLAY_TIMEZONE so admin tools, the legacy client wrapper in src/lib/format.ts, and audit-log viewers keep behaving identically. Three tests cover Asia/Tokyo wall-clock + date roll-over and the empty-string fallback. No production callers yet — wire-up happens in the per-surface commits that follow (doctor-report PDF, Coach snapshot, chart axes).
This commit is contained in:
1 parent
4cb8ba3491
commit
3c608520f6
2 files changed
+33
-5
No files matched your search
@@ -61,4 +61,25 @@ describe("makeFormatters", () => {
|
||||
expect(deFmt.time(sample)).toBe("16:30");
|
||||
expect(enFmt.time(sample)).toBe("16:30");
|
||||
});
|
||||
|
||||
// v1.4.25 W7 — formatters accept a per-user timezone override.
|
||||
it("renders time in the user's tz when userTz is passed", () => {
|
||||
const tokyo = makeFormatters("en", "Asia/Tokyo");
|
||||
// 14:30 UTC = 23:30 Tokyo
|
||||
expect(tokyo.time(sample)).toBe("23:30");
|
||||
});
|
||||
|
||||
it("renders time in Europe/Berlin when userTz is empty", () => {
|
||||
const fallback = makeFormatters("en", "");
|
||||
expect(fallback.time(sample)).toBe("16:30");
|
||||
});
|
||||
|
||||
it("renders date in the user's tz when userTz is passed", () => {
|
||||
// 23:30 UTC on May 10 = 01:30 May 11 Tokyo
|
||||
const lateUtc = new Date("2026-05-10T23:30:00Z");
|
||||
const tokyo = makeFormatters("en", "Asia/Tokyo");
|
||||
expect(tokyo.date(lateUtc)).toMatch(/2026/);
|
||||
// The narrow check: day-of-month should be 11, not 10.
|
||||
expect(tokyo.date(lateUtc)).toMatch(/11/);
|
||||
});
|
||||
});
|
||||
@@ -7,14 +7,21 @@
|
||||
*
|
||||
* Call via the `useFormatters()` client hook. For server-only code paths
|
||||
* (jobs, API routes, PDF generation) pass the locale explicitly through
|
||||
* `makeFormatters(locale)`.
|
||||
* `makeFormatters(locale)`. The optional second argument carries the
|
||||
* per-user timezone (v1.4.25 W7) — when omitted the formatter falls back
|
||||
* to `DISPLAY_TIMEZONE` so legacy callers keep rendering in Europe/Berlin.
|
||||
*
|
||||
* Timezone is fixed to Europe/Berlin for display (user expectation). UTC is
|
||||
* preserved in the database.
|
||||
* UTC is always preserved in the database.
|
||||
*/
|
||||
|
||||
import type { Locale } from "./i18n/config";
|
||||
|
||||
/**
|
||||
* Fallback timezone for surfaces that are not yet user-scoped (admin
|
||||
* tables, audit log viewer, anything without a resolved user). User-
|
||||
* scoped surfaces should call `makeFormatters(locale, userTz)` with
|
||||
* the value from `resolveUserTimezone()`.
|
||||
*/
|
||||
export const DISPLAY_TIMEZONE = "Europe/Berlin";
|
||||
|
||||
/**
|
||||
@@ -58,9 +65,9 @@ export interface Formatters {
|
||||
monthShort: (value: DateInput) => string;
|
||||
}
|
||||
|
||||
export function makeFormatters(locale: Locale): Formatters {
|
||||
export function makeFormatters(locale: Locale, userTz?: string): Formatters {
|
||||
const intlLocale = resolveIntlLocale(locale);
|
||||
const tz = DISPLAY_TIMEZONE;
|
||||
const tz = userTz && userTz.length > 0 ? userTz : DISPLAY_TIMEZONE;
|
||||
|
||||
return {
|
||||
number: (value, fractionDigits) =>
|
||||
|
||||
Reference in new issue
Block a user