diff options
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)); }; |
