From 1ea0321da73f27aa07a81d481bd0115151cfd192 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Thu, 21 May 2026 18:49:06 +0200 Subject: [PATCH] refactor(insights): dedup BERLIN_DAY_FORMATTER across seven status helpers Each of the seven src/lib/insights/*-status.ts files carried an identical 20-LOC block defining BERLIN_DAY_FORMATTER plus toBerlinDayKey. Extract the helper into src/lib/tz/resolver.ts (next to dayKeyForScheduledFor and friends) and import it everywhere. Same runtime behaviour, 100-LOC drop. --- src/lib/insights/blood-pressure-status.ts | 21 +----------- src/lib/insights/bmi-status.ts | 21 +----------- src/lib/insights/general-status.ts | 21 +----------- .../insights/medication-compliance-status.ts | 21 +----------- src/lib/insights/mood-status.ts | 21 +----------- src/lib/insights/pulse-status.ts | 21 +----------- src/lib/insights/weight-status.ts | 21 +----------- src/lib/tz/resolver.ts | 32 +++++++++++++++++++ 8 files changed, 39 insertions(+), 140 deletions(-) diff --git a/src/lib/insights/blood-pressure-status.ts b/src/lib/insights/blood-pressure-status.ts index a8dca717e..4760438f8 100644 --- a/src/lib/insights/blood-pressure-status.ts +++ b/src/lib/insights/blood-pressure-status.ts @@ -30,29 +30,10 @@ import { } from "@/lib/insights/with-timeout"; import { persistTimeoutStubAndReturn } from "@/lib/insights/persist-timeout-stub"; import { annotate } from "@/lib/logging/context"; - -const BERLIN_DAY_FORMATTER = new Intl.DateTimeFormat("en-US", { - timeZone: "Europe/Berlin", - year: "numeric", - month: "2-digit", - day: "2-digit", -}); +import { toBerlinDayKey } from "@/lib/tz/resolver"; type SupportedLocale = "de" | "en"; -function toBerlinDayKey(date: Date): string { - const parts = BERLIN_DAY_FORMATTER.formatToParts(date); - const year = parts.find((part) => part.type === "year")?.value; - const month = parts.find((part) => part.type === "month")?.value; - const day = parts.find((part) => part.type === "day")?.value; - - if (!year || !month || !day) { - throw new Error("Could not derive Berlin day key"); - } - - return `${year}-${month}-${day}`; -} - function round(value: number, digits = 1): number { const factor = 10 ** digits; return Math.round(value * factor) / factor; diff --git a/src/lib/insights/bmi-status.ts b/src/lib/insights/bmi-status.ts index 697e91798..68313d8e6 100644 --- a/src/lib/insights/bmi-status.ts +++ b/src/lib/insights/bmi-status.ts @@ -15,29 +15,10 @@ import { } from "@/lib/insights/with-timeout"; import { persistTimeoutStubAndReturn } from "@/lib/insights/persist-timeout-stub"; import { annotate } from "@/lib/logging/context"; - -const BERLIN_DAY_FORMATTER = new Intl.DateTimeFormat("en-US", { - timeZone: "Europe/Berlin", - year: "numeric", - month: "2-digit", - day: "2-digit", -}); +import { toBerlinDayKey } from "@/lib/tz/resolver"; type SupportedLocale = "de" | "en"; -function toBerlinDayKey(date: Date): string { - const parts = BERLIN_DAY_FORMATTER.formatToParts(date); - const year = parts.find((part) => part.type === "year")?.value; - const month = parts.find((part) => part.type === "month")?.value; - const day = parts.find((part) => part.type === "day")?.value; - - if (!year || !month || !day) { - throw new Error("Could not derive Berlin day key"); - } - - return `${year}-${month}-${day}`; -} - function round(value: number, digits = 1): number { const factor = 10 ** digits; return Math.round(value * factor) / factor; diff --git a/src/lib/insights/general-status.ts b/src/lib/insights/general-status.ts index 0d4145520..598d17f3b 100644 --- a/src/lib/insights/general-status.ts +++ b/src/lib/insights/general-status.ts @@ -20,13 +20,7 @@ import { } from "@/lib/insights/with-timeout"; import { persistTimeoutStubAndReturn } from "@/lib/insights/persist-timeout-stub"; import { annotate } from "@/lib/logging/context"; - -const BERLIN_DAY_FORMATTER = new Intl.DateTimeFormat("en-US", { - timeZone: "Europe/Berlin", - year: "numeric", - month: "2-digit", - day: "2-digit", -}); +import { toBerlinDayKey } from "@/lib/tz/resolver"; type SupportedLocale = "de" | "en"; @@ -34,19 +28,6 @@ type SupportedLocale = "de" | "en"; // in the AI general-status fetch (V3 audit: enum drift cousins). const MEASUREMENT_TYPES = measurementTypeEnum.options; -function toBerlinDayKey(date: Date): string { - const parts = BERLIN_DAY_FORMATTER.formatToParts(date); - const year = parts.find((part) => part.type === "year")?.value; - const month = parts.find((part) => part.type === "month")?.value; - const day = parts.find((part) => part.type === "day")?.value; - - if (!year || !month || !day) { - throw new Error("Could not derive Berlin day key"); - } - - return `${year}-${month}-${day}`; -} - function round(value: number, digits = 1): number { const factor = 10 ** digits; return Math.round(value * factor) / factor; diff --git a/src/lib/insights/medication-compliance-status.ts b/src/lib/insights/medication-compliance-status.ts index bbf6e5096..83be27b21 100644 --- a/src/lib/insights/medication-compliance-status.ts +++ b/src/lib/insights/medication-compliance-status.ts @@ -20,18 +20,12 @@ import { } from "@/lib/insights/with-timeout"; import { persistTimeoutStubAndReturn } from "@/lib/insights/persist-timeout-stub"; import { annotate } from "@/lib/logging/context"; +import { toBerlinDayKey } from "@/lib/tz/resolver"; // 360 daily days + 24 monthly windows ≈ 1080 days of intake history. const COMPLIANCE_HISTORY_DAYS = 360 + 24 * 30; const MS_PER_DAY = 24 * 60 * 60 * 1000; -const BERLIN_DAY_FORMATTER = new Intl.DateTimeFormat("en-US", { - timeZone: "Europe/Berlin", - year: "numeric", - month: "2-digit", - day: "2-digit", -}); - type SupportedLocale = "de" | "en"; interface MedicationSummaryItem { @@ -39,19 +33,6 @@ interface MedicationSummaryItem { text: string; } -function toBerlinDayKey(date: Date): string { - const parts = BERLIN_DAY_FORMATTER.formatToParts(date); - const year = parts.find((part) => part.type === "year")?.value; - const month = parts.find((part) => part.type === "month")?.value; - const day = parts.find((part) => part.type === "day")?.value; - - if (!year || !month || !day) { - throw new Error("Could not derive Berlin day key"); - } - - return `${year}-${month}-${day}`; -} - function round(value: number, digits = 1): number { const factor = 10 ** digits; return Math.round(value * factor) / factor; diff --git a/src/lib/insights/mood-status.ts b/src/lib/insights/mood-status.ts index 5c2cecdc4..a13d5c820 100644 --- a/src/lib/insights/mood-status.ts +++ b/src/lib/insights/mood-status.ts @@ -22,29 +22,10 @@ import { } from "@/lib/insights/with-timeout"; import { persistTimeoutStubAndReturn } from "@/lib/insights/persist-timeout-stub"; import { annotate } from "@/lib/logging/context"; - -const BERLIN_DAY_FORMATTER = new Intl.DateTimeFormat("en-US", { - timeZone: "Europe/Berlin", - year: "numeric", - month: "2-digit", - day: "2-digit", -}); +import { toBerlinDayKey } from "@/lib/tz/resolver"; type SupportedLocale = "de" | "en"; -function toBerlinDayKey(date: Date): string { - const parts = BERLIN_DAY_FORMATTER.formatToParts(date); - const year = parts.find((part) => part.type === "year")?.value; - const month = parts.find((part) => part.type === "month")?.value; - const day = parts.find((part) => part.type === "day")?.value; - - if (!year || !month || !day) { - throw new Error("Could not derive Berlin day key"); - } - - return `${year}-${month}-${day}`; -} - function round(value: number, digits = 1): number { const factor = 10 ** digits; return Math.round(value * factor) / factor; diff --git a/src/lib/insights/pulse-status.ts b/src/lib/insights/pulse-status.ts index a84d190cc..bf78c9e24 100644 --- a/src/lib/insights/pulse-status.ts +++ b/src/lib/insights/pulse-status.ts @@ -21,29 +21,10 @@ import { } from "@/lib/insights/with-timeout"; import { persistTimeoutStubAndReturn } from "@/lib/insights/persist-timeout-stub"; import { annotate } from "@/lib/logging/context"; - -const BERLIN_DAY_FORMATTER = new Intl.DateTimeFormat("en-US", { - timeZone: "Europe/Berlin", - year: "numeric", - month: "2-digit", - day: "2-digit", -}); +import { toBerlinDayKey } from "@/lib/tz/resolver"; type SupportedLocale = "de" | "en"; -function toBerlinDayKey(date: Date): string { - const parts = BERLIN_DAY_FORMATTER.formatToParts(date); - const year = parts.find((part) => part.type === "year")?.value; - const month = parts.find((part) => part.type === "month")?.value; - const day = parts.find((part) => part.type === "day")?.value; - - if (!year || !month || !day) { - throw new Error("Could not derive Berlin day key"); - } - - return `${year}-${month}-${day}`; -} - function round(value: number, digits = 1): number { const factor = 10 ** digits; return Math.round(value * factor) / factor; diff --git a/src/lib/insights/weight-status.ts b/src/lib/insights/weight-status.ts index d1e474c63..e1cab0b63 100644 --- a/src/lib/insights/weight-status.ts +++ b/src/lib/insights/weight-status.ts @@ -25,29 +25,10 @@ import { } from "@/lib/insights/with-timeout"; import { persistTimeoutStubAndReturn } from "@/lib/insights/persist-timeout-stub"; import { annotate } from "@/lib/logging/context"; - -const BERLIN_DAY_FORMATTER = new Intl.DateTimeFormat("en-US", { - timeZone: "Europe/Berlin", - year: "numeric", - month: "2-digit", - day: "2-digit", -}); +import { toBerlinDayKey } from "@/lib/tz/resolver"; type SupportedLocale = "de" | "en"; -function toBerlinDayKey(date: Date): string { - const parts = BERLIN_DAY_FORMATTER.formatToParts(date); - const year = parts.find((part) => part.type === "year")?.value; - const month = parts.find((part) => part.type === "month")?.value; - const day = parts.find((part) => part.type === "day")?.value; - - if (!year || !month || !day) { - throw new Error("Could not derive Berlin day key"); - } - - return `${year}-${month}-${day}`; -} - function round(value: number, digits = 1): number { const factor = 10 ** digits; return Math.round(value * factor) / factor; diff --git a/src/lib/tz/resolver.ts b/src/lib/tz/resolver.ts index 940321cf1..4465802a7 100644 --- a/src/lib/tz/resolver.ts +++ b/src/lib/tz/resolver.ts @@ -55,6 +55,38 @@ export { type FormatInUserTzShape, } from "./format"; +const BERLIN_DAY_FORMATTER = new Intl.DateTimeFormat("en-US", { + timeZone: "Europe/Berlin", + year: "numeric", + month: "2-digit", + day: "2-digit", +}); + +/** + * Format a Date as `YYYY-MM-DD` in `Europe/Berlin`. The seven Insight + * status helpers (`bmi`, `blood-pressure`, `weight`, `pulse`, `mood`, + * `medication-compliance`, `general`) all key their per-day cache on + * this — Marc's primary tenant runs in Berlin and the cache action + * row reads identically across helpers when the formatter lives here. + * + * For per-user surfaces, prefer `userDayKey(date, tz)` from `./format`. + * This helper exists for the cache-key path where the day-bucket is + * intentionally Berlin-anchored regardless of the requesting user's + * stored zone. + */ +export function toBerlinDayKey(date: Date): string { + const parts = BERLIN_DAY_FORMATTER.formatToParts(date); + const year = parts.find((part) => part.type === "year")?.value; + const month = parts.find((part) => part.type === "month")?.value; + const day = parts.find((part) => part.type === "day")?.value; + + if (!year || !month || !day) { + throw new Error("Could not derive Berlin day key"); + } + + return `${year}-${month}-${day}`; +} + const TTL_MS = 60_000; interface CacheEntry {