What's broken
hooks/ReminderRouter.hook.ts derives due dates with:
function iso(d: Date): string {
return d.toISOString().slice(0, 10);
}
toISOString() is always UTC, so between 21:00 and 23:59 in UTC-3 (and any negative-offset zone's evening), "remind me today" files an issue due tomorrow. It fails silently every night — the issue is created, the date is just wrong, so nothing ever alarms.
Version / location
- v7.40.4 (current tag),
LifeOS/install/hooks/ReminderRouter.hook.ts:122-124 (iso()), used by parseRelativeDate for today/tomorrow/in-N-days/next-weekday.
- The class is wider:
grep -rn "toISOString().slice(0, 10)\|toISOString().split" hooks/ LIFEOS/TOOLS/ LIFEOS/PULSE/ finds ~33 sites at the tag; most are human-facing days (due dates, created: frontmatter, "Last Updated" stamps, filenames, "Today is X" prompts to models) where the UTC day is the wrong answer for any non-UTC operator.
Repro (clean tree, no patches)
// bun repro.ts — uses only the tag's code path
const now = new Date("2026-08-01T01:06:00Z"); // == 2026-07-31 22:06 in America/Montevideo
console.log(now.toISOString().slice(0, 10)); // "2026-08-01" — but the operator's "today" is 2026-07-31
Negative control: the same derivation at midday (2026-08-20T15:00:00Z, 12:00 UTC-3) returns the correct local day — which is why the bug survives casual testing: it only exists at night, and its symptom is a reminder that quietly arrives a day late.
Root cause
The codebase has no primitive for "the LOCAL calendar day of an instant". hooks/lib/time.ts already reads principal.timezone from settings.json and has getPSTDate() (local today), but nothing converts an arbitrary Date to the principal's day, so call sites reach for toISOString().slice(0, 10).
Suggested fix
Add the missing primitive to hooks/lib/time.ts (timezone as a parameter so it's testable — bun test runs in UTC, where an environment-reading version can never fail):
export function localISODate(date: Date = new Date(), timezone: string = getTimezone()): string {
return new Intl.DateTimeFormat('en-CA', {
timeZone: timezone, year: 'numeric', month: '2-digit', day: '2-digit',
}).format(date);
}
Then migrate the human-facing sites (iso() in ReminderRouter first). Two classes of site should NOT be converted: absolute event seals (full-ISO ts fields in logs) and pure date-string arithmetic that anchors on T00:00:00Z and returns to a string (e.g. AppleHealthImport.windowStart).
Running this fix in production since 2026-08-20 (26 files migrated, test suite green, regression tests red against the unpatched derivation). Happy to PR if useful.
Prior art
What's broken
hooks/ReminderRouter.hook.tsderives due dates with:toISOString()is always UTC, so between 21:00 and 23:59 in UTC-3 (and any negative-offset zone's evening), "remind me today" files an issue due tomorrow. It fails silently every night — the issue is created, the date is just wrong, so nothing ever alarms.Version / location
LifeOS/install/hooks/ReminderRouter.hook.ts:122-124(iso()), used byparseRelativeDatefor today/tomorrow/in-N-days/next-weekday.grep -rn "toISOString().slice(0, 10)\|toISOString().split" hooks/ LIFEOS/TOOLS/ LIFEOS/PULSE/finds ~33 sites at the tag; most are human-facing days (due dates,created:frontmatter, "Last Updated" stamps, filenames, "Today is X" prompts to models) where the UTC day is the wrong answer for any non-UTC operator.Repro (clean tree, no patches)
Negative control: the same derivation at midday (
2026-08-20T15:00:00Z, 12:00 UTC-3) returns the correct local day — which is why the bug survives casual testing: it only exists at night, and its symptom is a reminder that quietly arrives a day late.Root cause
The codebase has no primitive for "the LOCAL calendar day of an instant".
hooks/lib/time.tsalready readsprincipal.timezonefrom settings.json and hasgetPSTDate()(local today), but nothing converts an arbitrary Date to the principal's day, so call sites reach fortoISOString().slice(0, 10).Suggested fix
Add the missing primitive to
hooks/lib/time.ts(timezone as a parameter so it's testable —bun testruns in UTC, where an environment-reading version can never fail):Then migrate the human-facing sites (
iso()in ReminderRouter first). Two classes of site should NOT be converted: absolute event seals (full-ISOtsfields in logs) and pure date-string arithmetic that anchors onT00:00:00Zand returns to a string (e.g.AppleHealthImport.windowStart).Running this fix in production since 2026-08-20 (26 files migrated, test suite green, regression tests red against the unpatched derivation). Happy to PR if useful.
Prior art
America/Los_Angeles: readings near local midnight filed under the wrong day for non-Pacific operators) — same family, accepted and fixed in fix(healthsync): resolve the operator's timezone instead of hardcoding America/Los_Angeles #1783. This is that bug's twin on the other side: not a hardcoded zone, but no zone at all.(new Date()).toISOString()#1077 was closed with "the cases where it doesn't [usetoISOString] are usually intentional … local-time semantics that ISO would obscure" and an invitation to "open a fresh issue … pointing at the file/line". This is that file and line, pointing the other way: a local-time semantic that ISO is obscuring.