diff options
| author | Martin Kagamino Lehoux <martin@lehoux.net> | 2026-09-08 09:56:55 +0200 |
|---|---|---|
| committer | Martin Kagamino Lehoux <martin@lehoux.net> | 2026-09-08 09:56:55 +0200 |
| commit | f6687db926e03eb44e5d72d321021f7738600357 (patch) | |
| tree | 3af3c99b7401c46aab5695bc7fa066621d0fd69d /src/lib/shared/routine-metrics.ts | |
| parent | 60475b2345b6a064fb4dee5259597d7e4569fdb7 (diff) | |
Anchor routine due dates on UTC calendar days
Daily routine done yesterday is due today regardless of time of day;
same anchoring in health series so chart and badge agree.
Diffstat (limited to 'src/lib/shared/routine-metrics.ts')
| -rw-r--r-- | src/lib/shared/routine-metrics.ts | 14 |
1 files changed, 12 insertions, 2 deletions
diff --git a/src/lib/shared/routine-metrics.ts b/src/lib/shared/routine-metrics.ts index 99e5c56..59413c2 100644 --- a/src/lib/shared/routine-metrics.ts +++ b/src/lib/shared/routine-metrics.ts @@ -1,8 +1,16 @@ // Pure routine metrics, shared by server services and UI. No db access. // All timestamps are ISO strings; all math is done in ms then converted to days. +// Due dates anchor on UTC calendar days, not exact 24h intervals: a daily +// routine done yesterday is due today, whatever the time of day. const DAY_MS = 24 * 60 * 60 * 1000; +/** Midnight UTC starting the calendar day containing ms. */ +function startOfUtcDay(ms: number): number { + const d = new Date(ms); + return Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate()); +} + export type RoutineStatus = 'ok' | 'due' | 'late' | 'veryLate'; export interface RoutineInput { @@ -48,7 +56,9 @@ export function computeRoutineState( const referenceMs = lastDoneMs ?? (Number.isFinite(createdMs) ? createdMs : now); const periodMs = routine.frequencyDays * DAY_MS; - const dueMs = referenceMs + periodMs; + // Calendar-day anchor: the period starts at midnight of the reference day, + // so any new calendar day past the frequency flips the routine to due. + const dueMs = startOfUtcDay(referenceMs) + periodMs; const rawRatio = (now - dueMs) / periodMs; const overdueRatio = Math.max(0, rawRatio); // Buckets on overdue time in units of the routine's own frequency: @@ -127,7 +137,7 @@ export function computeHealthSeries( refMs = completionMs[nextCompletion]; nextCompletion++; } - const dueMs = refMs + periodMs; + const dueMs = startOfUtcDay(refMs) + periodMs; const rawRatio = (t - dueMs) / periodMs; return Math.max(0, Math.round(100 - Math.max(0, rawRatio) * 100)); }; |
