mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 12:32:43 +00:00
refactor(tz): route day-key copies through the canonical userDayKey
Compliance streaks, the measurement-consolidation day key, and the correlations weekday helper each carried their own Intl formatter (en-CA / sv-SE / short-weekday). Delegate them to userDayKey and wallClockInTz so every day-bucket key stays byte-for-byte aligned and the locale decision lives in one place.
This commit is contained in:
1 parent
1b354de68b
commit
cbfc4a36d2
3 files changed
+17
-42
No files matched your search
@@ -58,6 +58,7 @@ import {
|
||||
type CorrelationResult,
|
||||
} from "@/lib/insights/correlations";
|
||||
import { isNearUtc, userDayKey } from "@/lib/tz/resolver";
|
||||
import { wallClockInTz } from "@/lib/tz/wall-clock";
|
||||
|
||||
/**
|
||||
* v1.4.37 W2 — cold-path correlation window. Trim from 30 to 28 days
|
||||
@@ -368,34 +369,8 @@ function dateFromDayKey(key: string): Date {
|
||||
return new Date(`${key}T00:00:00.000Z`);
|
||||
}
|
||||
|
||||
const WEEKDAY_FORMATTER_CACHE = new Map<string, Intl.DateTimeFormat>();
|
||||
function getWeekdayFormatter(timeZone: string): Intl.DateTimeFormat {
|
||||
let formatter = WEEKDAY_FORMATTER_CACHE.get(timeZone);
|
||||
if (!formatter) {
|
||||
formatter = new Intl.DateTimeFormat("en-CA", {
|
||||
timeZone,
|
||||
year: "numeric",
|
||||
month: "2-digit",
|
||||
day: "2-digit",
|
||||
weekday: "short",
|
||||
});
|
||||
WEEKDAY_FORMATTER_CACHE.set(timeZone, formatter);
|
||||
}
|
||||
return formatter;
|
||||
}
|
||||
|
||||
const ISO_WEEKDAY: Record<string, number> = {
|
||||
Mon: 1,
|
||||
Tue: 2,
|
||||
Wed: 3,
|
||||
Thu: 4,
|
||||
Fri: 5,
|
||||
Sat: 6,
|
||||
Sun: 7,
|
||||
};
|
||||
|
||||
function isoWeekdayInTz(d: Date, timeZone: string): number {
|
||||
const parts = getWeekdayFormatter(timeZone).formatToParts(d);
|
||||
const weekday = parts.find((p) => p.type === "weekday")?.value ?? "Mon";
|
||||
return ISO_WEEKDAY[weekday] ?? 1;
|
||||
// `wallClockInTz` returns 0=Sun..6=Sat; remap to ISO 1=Mon..7=Sun.
|
||||
const weekday = wallClockInTz(d, timeZone).weekday;
|
||||
return weekday === 0 ? 7 : weekday;
|
||||
}
|
||||
@@ -22,6 +22,9 @@
|
||||
* every existing import site keeps working unchanged.
|
||||
*/
|
||||
import type { MeasurementType } from "@/generated/prisma/client";
|
||||
// `userDayKey` lives in the pure `@/lib/tz/format` leaf (no Prisma /
|
||||
// node:module pull) so importing it keeps this dependency-free module clean.
|
||||
import { userDayKey } from "@/lib/tz/format";
|
||||
|
||||
/**
|
||||
* Canonical grace window shared by the cumulative + mean drains. Rows
|
||||
@@ -52,12 +55,12 @@ export interface PerSampleRow {
|
||||
|
||||
/**
|
||||
* Resolve the user's calendar-day key (`YYYY-MM-DD`) for a given
|
||||
* timestamp + timezone. Reuses the same `sv-SE` Intl formatting choice
|
||||
* the mood-entries path locked in v1.4.25 W7b so iOS-side and
|
||||
* server-side day-keys round-trip byte-identically.
|
||||
* timestamp + timezone. Delegates to the canonical `userDayKey` so the
|
||||
* server-side and iOS-side day-keys round-trip byte-identically against
|
||||
* every other day-bucketed surface.
|
||||
*/
|
||||
export function dayKeyForUserTz(date: Date, tz: string): string {
|
||||
return new Intl.DateTimeFormat("sv-SE", { timeZone: tz }).format(date);
|
||||
return userDayKey(date, tz);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -39,6 +39,7 @@ import {
|
||||
type RecurrenceContext,
|
||||
} from "./recurrence";
|
||||
import { normaliseDoseWindows } from "./worker-helpers";
|
||||
import { userDayKey } from "@/lib/tz/resolver";
|
||||
|
||||
export interface ComplianceChips {
|
||||
/** 0-100, taken / (taken + missed). Skipped doses are excluded from
|
||||
@@ -62,9 +63,10 @@ const DAY_MS = 24 * 60 * 60 * 1000;
|
||||
|
||||
/**
|
||||
* Day-key in the user's timezone (or system-local when `tz` is
|
||||
* undefined). `Intl.DateTimeFormat` with `en-CA` reliably returns
|
||||
* `YYYY-MM-DD`, which sorts lexically and matches the chart
|
||||
* legend's `day` ticks.
|
||||
* undefined). Routes through the canonical `userDayKey` so the
|
||||
* `YYYY-MM-DD` bucket stays byte-for-byte aligned with every other
|
||||
* day-bucketed surface; the host-local branch covers the pre-per-user
|
||||
* callers that pass no zone.
|
||||
*/
|
||||
function localDayKey(d: Date, tz: string | undefined): string {
|
||||
if (!tz) {
|
||||
@@ -73,12 +75,7 @@ function localDayKey(d: Date, tz: string | undefined): string {
|
||||
const day = String(d.getDate()).padStart(2, "0");
|
||||
return `${y}-${m}-${day}`;
|
||||
}
|
||||
return new Intl.DateTimeFormat("en-CA", {
|
||||
timeZone: tz,
|
||||
year: "numeric",
|
||||
month: "2-digit",
|
||||
day: "2-digit",
|
||||
}).format(d);
|
||||
return userDayKey(d, tz);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in new issue
Block a user