diff options
| author | Peter Stone <thepeterstone@gmail.com> | 2026-08-06 01:50:56 +0000 |
|---|---|---|
| committer | Peter Stone <thepeterstone@gmail.com> | 2026-08-06 01:50:56 +0000 |
| commit | c8ebaba6e04169359d84e00ffa2d797f40bc077e (patch) | |
| tree | e3c34a3159fbb8d3c326c3b1d02cf73ad8e24656 /internal/api/google_calendar.go | |
| parent | 693ca6d6fb88453788139bcdeee9e7ed771f4c8d (diff) | |
Fix Google Calendar TimeMin using server time instead of display timezone
GetUpcomingEvents built its TimeMin cutoff from raw time.Now() (server/UTC
time), not the start of today in the configured display timezone. Google's
API filters TimeMin against each event's END time (exclusive), so for a
UTC-10 display timezone (Pacific/Honolulu) that cutoff landed mid-afternoon
the PREVIOUS Hawaii-local day, not midnight of the actual display day.
Confirmed against live data before fixing, not assumed: the cached
calendar_events table's earliest row was "2026-08-05 15:00:00-10:00" --
exactly the mid-afternoon-yesterday skew this bug produces -- with a
complete gap for all of 2026-08-06 (today). This is what caused two
symptoms reported against the widget: already-ended-today events missing
entirely (never fetched into the cache in the first place, not a
client-side rendering bug -- the 2026-08-05 Android "past events" feature
was built correctly but had nothing to render) and the tomorrow section
appearing empty (same skewed data window scrambling the day-bucketing
downstream).
Fix: compute TimeMin from time.Now().In(displayTZ)'s start-of-day instead.
Added TestGetUpcomingEvents_TimeMinIsStartOfTodayInDisplayTimezone, which
asserts the actual outgoing timeMin query parameter is midnight-in-tz and
lands on today's date -- verified it catches the regression by reverting
to raw time.Now() against a real backup and confirming the exact failure
mode (captured timeMin = mid-afternoon the day before), then restored.
go test ./... -race is green. Deployed. Note: the calendar cache only
refreshes when the web dashboard is visited (aggregateData) -- the widget's
own refresh only re-reads the DB cache, never triggers a live Calendar
resync -- so the currently-cached stale data needs one dashboard visit to
actually reflect this fix, not just the deploy.
Diffstat (limited to 'internal/api/google_calendar.go')
| -rw-r--r-- | internal/api/google_calendar.go | 19 |
1 files changed, 18 insertions, 1 deletions
diff --git a/internal/api/google_calendar.go b/internal/api/google_calendar.go index 44457bf..a69f734 100644 --- a/internal/api/google_calendar.go +++ b/internal/api/google_calendar.go @@ -117,7 +117,24 @@ func NewGoogleCalendarClient(ctx context.Context, credentialsFile, calendarIDs, } func (c *GoogleCalendarClient) GetUpcomingEvents(ctx context.Context, maxResults int) ([]models.CalendarEvent, error) { - t := time.Now().Format(time.RFC3339) + // TimeMin must be the start of today in the DISPLAY timezone, not raw + // time.Now() (server/UTC time). Google's API filters TimeMin against + // each event's END time (exclusive), so this is also what makes an + // already-ended-today event "past" vs excluded entirely -- using + // time.Now() here meant, for a UTC-10 display timezone, the cutoff + // landed mid-afternoon the PREVIOUS Hawaii-local day (server midnight + // UTC is 2pm HST the day before), not midnight of the actual display + // day. That's what caused the 2026-08-06 report: past-today events + // missing entirely (silently never fetched into the cache in the first + // place, not a client-side bug) and the whole today/tomorrow bucketing + // downstream skewed by the same ~10h gap. + tz := c.displayTZ + if tz == nil { + tz = time.UTC + } + nowInTZ := time.Now().In(tz) + todayStart := time.Date(nowInTZ.Year(), nowInTZ.Month(), nowInTZ.Day(), 0, 0, 0, 0, tz) + t := todayStart.Format(time.RFC3339) fetchOne := func(calendarID string) []models.CalendarEvent { events, err := c.srv.Events.List(calendarID).ShowDeleted(false). |
