1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
---
name: reference-google-tasks-due-date-utc-quirk
description: "Google Tasks API's due date field is date-only but always serialized as midnight UTC -- converting the instant into a display timezone (vs. re-anchoring the date) shifts dates in negative-offset zones"
metadata:
node_type: memory
type: reference
originSessionId: 647d8c60-e602-440e-b8b3-d8422fcbcb1d
---
The Google Tasks API's `due` field is conceptually date-only (no time-of-day is meaningful), but the API always serializes it as an RFC3339 timestamp fixed at midnight UTC, regardless of the user's actual timezone. E.g. a task due "July 29" comes back as `"2026-07-29T00:00:00.000Z"` even for a user in Honolulu.
**The bug this causes:** if code does `dueDate.In(displayTZ)` on that parsed instant (converting the moment-in-time into the display timezone) instead of re-anchoring the same Y/M/D to local midnight, the date silently shifts backward for any timezone behind UTC. Midnight UTC becomes 2pm the *previous* day in UTC-10 (Pacific/Honolulu, doot's configured `config.GetDisplayTimezone()`) -- so every dated Google Task landed one calendar day earlier than Google actually reported. Symptom: "I have a scheduled task for tomorrow that's showing up today."
**The fix** (`internal/api/google_tasks.go`, `getTasksFromList`): extract `y, m, d := dueDate.Date()` from the UTC-parsed timestamp (that Y/M/D *is* the intended calendar date) and rebuild `time.Date(y, m, d, 0, 0, 0, 0, displayTZ)` from those components -- never call `.In(displayTZ)` on a Google Tasks due-date instant directly. Fixed 2026-07-28, regression-tested in `google_tasks_test.go` (`TestGetTasksFromList_DueDateNotShiftedByNegativeOffsetTZ`) using the real Pacific/Honolulu location via a mocked HTTP tasks-list response.
**How to apply:** if any *other* date-only field from an external API gets added to doot (a new integration, or a different Google Tasks/Calendar field), check whether that field has the same "date-only-but-serialized-as-UTC-midnight" convention before doing a naive `.In(displayTZ)` conversion on it. This is a genuinely common API convention (RFC3339 doesn't have a "date-only" type), not unique to Google Tasks.
|