From f3a4da50f6f600d18d29eab67e9b41e32740b36c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Thu, 21 May 2026 12:07:16 +0200 Subject: [PATCH] chore(glucose,berlin-day): drop unreferenced mmolToMgdl and berlinDayKey exports The glucose UI only converts canonical mg/dL into mmol/L for display via convertGlucose; the reverse helper had no caller outside its own test. The Berlin-only day-key helper was already superseded by userDayKey(date, tz) in v1.4.38 for every read path that consumed it; its remaining presence as a tested module was dead weight. --- src/app/api/analytics/route.ts | 9 ++--- src/lib/__tests__/glucose.test.ts | 13 ------- .../analytics/__tests__/berlin-day.test.ts | 31 ---------------- src/lib/analytics/berlin-day.ts | 35 ------------------- src/lib/glucose.ts | 4 --- src/lib/tz/format.ts | 12 +++---- 6 files changed, 9 insertions(+), 95 deletions(-) delete mode 100644 src/lib/analytics/__tests__/berlin-day.test.ts delete mode 100644 src/lib/analytics/berlin-day.ts diff --git a/src/app/api/analytics/route.ts b/src/app/api/analytics/route.ts index 2f72b8171..6db4d3e1b 100644 --- a/src/app/api/analytics/route.ts +++ b/src/app/api/analytics/route.ts @@ -208,12 +208,9 @@ async function buildAnalyticsResponse(user: AuthedUser) { // cost flat across the three branches. const coverage = await probeRollupCoverage(user.id); - // v1.4.25 W7b — every day-bucket call inside this route now honours - // the user's display timezone. The legacy `berlinDayKey()` import - // remains for sleep-stage and correlation paths that share their - // helper signature with non-tz-aware code (`computeSleepStageBreakdown` - // is called with a userId only); the per-call sites below all pass - // `userTz` through `userDayKey()`. + // v1.4.25 W7b — every day-bucket call inside this route honours the + // user's display timezone via `userDayKey(date, userTz)` (the legacy + // `berlinDayKey()` helper was retired in v1.4.40). const userTz = user.timezone ?? DEFAULT_TIMEZONE; // Derived from canonical enum so a new measurement type is auto-summarised diff --git a/src/lib/__tests__/glucose.test.ts b/src/lib/__tests__/glucose.test.ts index 1683af83b..40040b3c7 100644 --- a/src/lib/__tests__/glucose.test.ts +++ b/src/lib/__tests__/glucose.test.ts @@ -1,7 +1,6 @@ import { describe, it, expect } from "vitest"; import { mgdlToMmol, - mmolToMgdl, convertGlucose, resolveGlucoseUnit, thresholdMetricForContext, @@ -14,18 +13,6 @@ describe("glucose conversion", () => { expect(mgdlToMmol(70)).toBeCloseTo(3.9, 1); // hypoglycemia threshold }); - it("converts mmol/L to mg/dL rounded to integer", () => { - expect(mmolToMgdl(5.5)).toBe(99); - expect(mmolToMgdl(7.0)).toBe(126); - }); - - it("roundtrips within 1 mg/dL", () => { - for (const mgdl of [70, 99, 100, 126, 140, 200, 300]) { - const back = mmolToMgdl(mgdlToMmol(mgdl)); - expect(Math.abs(back - mgdl)).toBeLessThanOrEqual(1); - } - }); - it("convertGlucose dispatches to the right unit", () => { expect(convertGlucose(100, "mg/dL")).toBe(100); expect(convertGlucose(100, "mmol/L")).toBe(5.5); diff --git a/src/lib/analytics/__tests__/berlin-day.test.ts b/src/lib/analytics/__tests__/berlin-day.test.ts deleted file mode 100644 index 2ea78ae06..000000000 --- a/src/lib/analytics/__tests__/berlin-day.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { describe, it, expect } from "vitest"; -import { berlinDayKey } from "../berlin-day"; - -/** - * v1.4.22 W5 reconcile (Code-MED-3) — pin that the helper buckets a - * 23:30-Berlin reading on Tuesday into the Tuesday key, regardless of - * UTC offset (CET / CEST, DST). - */ -describe("berlinDayKey", () => { - it("buckets a 23:30-Berlin reading on Tuesday into Tuesday's key (CEST)", () => { - // 2025-10-25T21:30:00Z = 2025-10-25T23:30 CEST (Saturday before DST end) - expect(berlinDayKey(new Date("2025-10-25T21:30:00Z"))).toBe("2025-10-25"); - }); - - it("buckets a 23:30-Berlin reading after DST end into the right Berlin day", () => { - // 2025-10-26 is the DST end day in Europe/Berlin (CET takes over). - // 2025-10-26T22:30:00Z = 2025-10-26T23:30 CET — still Sunday in Berlin. - expect(berlinDayKey(new Date("2025-10-26T22:30:00Z"))).toBe("2025-10-26"); - }); - - it("crosses the UTC-midnight boundary in Berlin's favour", () => { - // 2026-05-09T22:30:00Z falls on 2026-05-10T00:30 CEST — Berlin says - // Sunday, UTC says Saturday. The helper must follow Berlin. - expect(berlinDayKey(new Date("2026-05-09T22:30:00Z"))).toBe("2026-05-10"); - }); - - it("returns YYYY-MM-DD format (sortable, padded)", () => { - expect(berlinDayKey(new Date("2026-01-09T12:00:00Z"))).toBe("2026-01-09"); - expect(berlinDayKey(new Date("2026-12-09T12:00:00Z"))).toBe("2026-12-09"); - }); -}); diff --git a/src/lib/analytics/berlin-day.ts b/src/lib/analytics/berlin-day.ts deleted file mode 100644 index 639022be9..000000000 --- a/src/lib/analytics/berlin-day.ts +++ /dev/null @@ -1,35 +0,0 @@ -/** - * v1.4.22 W5 reconcile (Code-MED-1) — shared Berlin-day-key helper. - * - * The CLAUDE.md timezone convention is "Europe/Berlin for display, - * UTC in database". The dashboard analytics route used a private - * `berlinDayKey()` (defined in `src/app/api/analytics/route.ts:330`) - * to bucket measurements by Berlin calendar day. The targets route's - * `sparklinePoints()` was bucketing by UTC `YYYY-MM-DD` — a 23:30 - * Berlin reading on Tuesday landed in Wednesday's bucket, drifting - * the sparkline 1 day on cross-DST boundaries. - * - * Lift the helper into `src/lib/analytics/` so every display surface - * shares the same bucket contract. - */ - -const BERLIN_DATE_PARTS = new Intl.DateTimeFormat("en-CA", { - timeZone: "Europe/Berlin", - year: "numeric", - month: "2-digit", - day: "2-digit", -}); - -/** - * Format a `Date` as a `YYYY-MM-DD` string anchored to Europe/Berlin - * calendar day. Used as a stable key for daily aggregation so a - * 23:30-Berlin reading on Tuesday lands in Tuesday's bucket - * regardless of UTC offset (CET / CEST, DST). - */ -export function berlinDayKey(d: Date): string { - const parts = BERLIN_DATE_PARTS.formatToParts(d); - const y = parts.find((p) => p.type === "year")?.value ?? "1970"; - const m = parts.find((p) => p.type === "month")?.value ?? "01"; - const day = parts.find((p) => p.type === "day")?.value ?? "01"; - return `${y}-${m}-${day}`; -} diff --git a/src/lib/glucose.ts b/src/lib/glucose.ts index 21cbe798d..a794376a6 100644 --- a/src/lib/glucose.ts +++ b/src/lib/glucose.ts @@ -15,10 +15,6 @@ export function mgdlToMmol(mgdl: number): number { return Math.round((mgdl / MGDL_PER_MMOL) * 10) / 10; } -export function mmolToMgdl(mmol: number): number { - return Math.round(mmol * MGDL_PER_MMOL); -} - export function convertGlucose(value: number, to: GlucoseUnit): number { return to === "mmol/L" ? mgdlToMmol(value) : Math.round(value); } diff --git a/src/lib/tz/format.ts b/src/lib/tz/format.ts index 46d9cd65a..112c65dcc 100644 --- a/src/lib/tz/format.ts +++ b/src/lib/tz/format.ts @@ -121,7 +121,7 @@ export type FormatInUserTzShape = * `DISPLAY_TIMEZONE`. * * - "date" — `2026-05-11` (ISO date in the user's zone). Used as a - * day-bucket key parallel to the legacy `berlinDayKey()`. + * stable day-bucket key for per-user aggregation. */ export function formatInUserTz( date: Date, @@ -151,11 +151,11 @@ export function formatInUserTz( } /** - * Day-bucket key in the user's timezone. Mirrors `berlinDayKey()` but - * parameterised. Used as a stable Map key for daily aggregation so a - * 23:30-local reading lands on today's bucket regardless of UTC - * offset. The legacy `berlinDayKey()` stays for surfaces that are - * not yet user-scoped (admin tables, audit log viewer). + * Day-bucket key in the user's timezone. Used as a stable Map key for + * daily aggregation so a 23:30-local reading lands on today's bucket + * regardless of UTC offset. Every display surface in v1.4.40 anchors + * here — the legacy Berlin-only `berlinDayKey()` was retired in that + * release. */ export function userDayKey(date: Date, tz: string): string { return formatInUserTz(date, tz, "date");