Skip to content

history time group_by:day mixes UTC date bounds with local day labels #385

Description

@moonray

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

  1. User in a non-UTC timezone (e.g., US-Pacific).
  2. A session with a turn at, say, 2026-07-04 02:00Z (= 2026-07-03 19:00 Pacific).
  3. 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: parseDateBoundiso8601DateOnly.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

  • Date-only date_from/date_to resolve in the user's local timezone (matching Calendar.current day-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.
  • Regression test using a turn near midnight UTC that falls on a different local day.
  • Spec updated (docs/spec/history-query-tools.md, date-bounds section: UTC → local).

Detail / links

Source

Surfaced during user-testing the history MCP tool (2026-07-05).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions