Problem
Several places use someDate.toISOString().split('T')[0] to produce a YYYY-MM-DD value for <input type="date"> (or for date filters). toISOString() returns the UTC calendar date, but <input type="date"> is rendered in the user's local calendar. The two diverge by a day for any user whose local clock has crossed midnight UTC (so e.g. a US west-coast user after ~4-5pm local sees defaults / mins resolve to "tomorrow" instead of "today").
PR #1258 fixed the two occurrences in frontend/src/plans.ts::openAddPurchasesModal via a new toLocalDateInputValue(Date) helper. The same pattern survives in:
frontend/src/history.ts:179 — startInput.value = start.toISOString().split('T')[0]
frontend/src/history.ts:182 — endInput.value = end.toISOString().split('T')[0]
frontend/src/history.ts:243-244 — minDate / maxDate of the analytics range
frontend/src/apikeys.ts:359 — default expiry date for new API keys
Impact
- Analytics filter defaults shift by a day for evening-hours users west of UTC. Visible as "I clicked default ‘last 30 days’ at 11pm and got 31 days back."
- API-key default expiry is off by a day on creation.
Severity is low — values are pre-fills, the user can fix them — but the inconsistency with the plans.ts fix invites future contributors to copy the wrong pattern.
Suggested fix
Promote toLocalDateInputValue from plans.ts to frontend/src/utils.ts (or a small dates.ts neighbour) and replace the four call sites above. Regression tests should mirror the QA 5.6 test in plans-range-validation.test.ts.
Acceptance criteria
Related
Problem
Several places use
someDate.toISOString().split('T')[0]to produce aYYYY-MM-DDvalue for<input type="date">(or for date filters).toISOString()returns the UTC calendar date, but<input type="date">is rendered in the user's local calendar. The two diverge by a day for any user whose local clock has crossed midnight UTC (so e.g. a US west-coast user after ~4-5pm local sees defaults / mins resolve to "tomorrow" instead of "today").PR #1258 fixed the two occurrences in
frontend/src/plans.ts::openAddPurchasesModalvia a newtoLocalDateInputValue(Date)helper. The same pattern survives in:frontend/src/history.ts:179—startInput.value = start.toISOString().split('T')[0]frontend/src/history.ts:182—endInput.value = end.toISOString().split('T')[0]frontend/src/history.ts:243-244—minDate/maxDateof the analytics rangefrontend/src/apikeys.ts:359— default expiry date for new API keysImpact
Severity is low — values are pre-fills, the user can fix them — but the inconsistency with the
plans.tsfix invites future contributors to copy the wrong pattern.Suggested fix
Promote
toLocalDateInputValuefromplans.tstofrontend/src/utils.ts(or a smalldates.tsneighbour) and replace the four call sites above. Regression tests should mirror the QA 5.6 test inplans-range-validation.test.ts.Acceptance criteria
frontend/src/utils.ts(or similar).plans.tsswitches from the local helper to the shared one.grep -rE "toISOString\\(\\)\\.split\\('T'\\)\\[0\\]" frontend/srcreturns nothing outside tests.Related
fix/qa437-purchase-ui-polish, commit43c395440).