define calendar-date and timezone semantics for task due dates
Due dates are presented as calendar dates, but storage retains a timestamp without the selected IANA timezone or an explicit calendar-date contract. PR #1762 fixes reminders firing one day early by treating the stored timestamp plus 24 hours as expiration.
That approximation leaves two cases unresolved:
- A local due day can last 23 or 25 hours across daylight-saving changes, so adding 24 hours can place reminders and overdue notifications an hour away from the intended local midnight.
- REST API, MCP, and import inputs can contain non-midnight timestamps. For example,
2026-04-05T17:00:00Zcould mean an explicit time or April 6 at midnight in Asia/Bangkok. The stored value cannot distinguish these intentions, so truncating it to UTC midnight can change the selected date.
Define the calendar-date and timezone policy before changing the calculation. Decide which timezone governs expiration and preserve enough information to derive the following local midnight. Plan compatibility for existing tasks and consistent behavior across the UI, REST API, MCP, imports, personal reminders, overdue notifications, and project webhooks.
Acceptance criteria:
- Document the due-date input, storage, and expiration contract.
- Preserve existing selected dates through any migration or compatibility handling.
- Calculate expiration with calendar arithmetic under the chosen timezone policy.
- Cover spring-forward and fall-back days, positive and negative UTC offsets, and non-midnight API input in regression tests.
- Keep reminder deduplication and indexed overdue lookups intact.
Follow-up to #1756 and #1762; this does not block the focused one-day-early reminder correction.
Originally requested by @tinsever on 2026-09-25. Original GitHub request #1783.
As a drive-by comment, we're dealing with a lot of date handling in our SaaS at work and we switched from using
Dateto Temporal (with a polyfill liketemporal-polyfill) and it makes this much, much easier. With Node 26+ it's now natively available server and client side. It's probably a massive refactor, but I'm just mentioning it here sinceTemporal.PlainDatehandles all the complexity of DST and so on if you only care about the date for example.Original GitHub comment