What
history.time with group_by:"day" (or week/month) applies date-only date_from/date_to bounds in UTC but labels day groups in the local timezone, so a query scoped to a single date can return groups labeled the previous day.
Repro
- User in a non-UTC timezone (e.g., US-Pacific).
- A session with a turn at, say,
2026-07-04 02:00Z (= 2026-07-03 19:00 Pacific).
history.time(group_by:"day", date_from:"2026-07-04", date_to:"2026-07-04").
Actual: returns a group labeled 2026-07-03. Expected: the turn belongs to local day 2026-07-03, so a 2026-07-04 (local) range should exclude it — i.e., bounds and labels should agree on the timezone.
Root cause
- Source timestamps are timezone-naive instants (correct).
- Day-group labels use local time:
Calendar.current.startOfDay(for: turn.startedAt) in HistoryMCPToolService.executeTimeCalendarGrouped.
- Date-only bounds parse as UTC midnight:
parseDateBound → iso8601DateOnly.date(from:), and ISO8601DateFormatter defaults to UTC.
So the bound is UTC-midnight while the label is local-day → mismatch. A turn at 2026-07-04 02:00Z passes the 2026-07-04 00:00Z bound but is labeled 2026-07-03 (its Pacific day). Affects day/week/month grouping. Datetime inputs with an explicit offset/Z are unaffected (exact instants).
Fix
Give the date-only bound parser the local timezone (e.g., a dedicated formatter with timeZone = Calendar.current.timeZone, so "2026-07-04" resolves to local midnight), making bounds match the local-day labels. Formatting group keys with the same local-timezone formatter would make it fully consistent.
Small change, but it:
- changes the documented bound contract (the history spec currently says date-only bounds resolve to
00:00:00 UTC),
- needs test updates for any UTC-bound assertions, and
- affects users in every timezone.
Acceptance criteria
Detail / links
Source
Surfaced during user-testing the history MCP tool (2026-07-05).
What
history.timewithgroup_by:"day"(orweek/month) applies date-onlydate_from/date_tobounds in UTC but labels day groups in the local timezone, so a query scoped to a single date can return groups labeled the previous day.Repro
2026-07-04 02:00Z(=2026-07-03 19:00Pacific).history.time(group_by:"day", date_from:"2026-07-04", date_to:"2026-07-04").Actual: returns a group labeled
2026-07-03. Expected: the turn belongs to local day2026-07-03, so a2026-07-04(local) range should exclude it — i.e., bounds and labels should agree on the timezone.Root cause
Calendar.current.startOfDay(for: turn.startedAt)inHistoryMCPToolService.executeTimeCalendarGrouped.parseDateBound→iso8601DateOnly.date(from:), andISO8601DateFormatterdefaults to UTC.So the bound is UTC-midnight while the label is local-day → mismatch. A turn at
2026-07-04 02:00Zpasses the2026-07-04 00:00Zbound but is labeled2026-07-03(its Pacific day). Affectsday/week/monthgrouping. Datetime inputs with an explicit offset/Zare unaffected (exact instants).Fix
Give the date-only bound parser the local timezone (e.g., a dedicated formatter with
timeZone = Calendar.current.timeZone, so"2026-07-04"resolves to local midnight), making bounds match the local-day labels. Formatting group keys with the same local-timezone formatter would make it fully consistent.Small change, but it:
00:00:00 UTC),Acceptance criteria
date_from/date_toresolve in the user's local timezone (matchingCalendar.currentday-group labels); datetime-with-offset inputs remain exact instants.time group_by:"day"with a single-date range returns groups whose label matches the requested date (no off-by-one) for users in non-UTC timezones.docs/spec/history-query-tools.md, date-bounds section: UTC → local).Detail / links
2026-07-04day query returned a2026-07-03group).Sources/RepoPrompt/Features/AgentMode/History/HistoryMCPToolService.swift—parseDateBound, theiso8601DateOnlyformatter, andexecuteTimeCalendarGrouped.Source
Surfaced during user-testing the history MCP tool (2026-07-05).