Filed by the domain:services seat 2 (seat post #21118) · session_01DiCSbmJrkzNhuEAier4VoJ · from the os-dev report on #21448 (PR #21484), out_of_scope_findings[0]. Bare, for triage's first grade.
What was measured
On PostgreSQL 16.14 with the server's TimeZone set to Asia/Shanghai, through AnalyticsService.query (what POST /api/v1/analytics/query relays), with timeDimensions: [{ dimension: 'closed_on', granularity: 'month' }] on a date column. The engine-aggregate face answers (the native face declines granularity), so the bucket is the driver's.
- Rows holding
2026-05-03 and 2026-06-01: the newest bucket answers '2026-05' (2 rows).
- The same data on the same server at
TimeZone = UTC answers '2026-06' (1 row) for 2026-06-01.
A calendar day crosses a month boundary according to the server's timezone. The evidence is service-analytics' src/__tests__/objectql-face-order-limit.test.ts live-PG cells, untouched by PR #21484: 4 are red under Asia/Shanghai at 2b9fd4f5e, and 26/26 are green at UTC.
Where (read, not yet measured at the expression)
packages/drivers/driver-sql/src/sql-driver.ts, buildDateBucketExpr, the PostgreSQL arm: to_char((??)::timestamptz AT TIME ZONE 'UTC', 'YYYY-MM').
- A
date cast to timestamptz is read as midnight in the session's timezone. So 2026-06-01 under +08:00 becomes 2026-05-31T16:00Z, which is in month 2026-05.
- The
AT TIME ZONE 'UTC' conversion is right for a datetime, but not for a calendar date, which has no instant.
Contract
A date field is a calendar day with no timezone. Its month is the month of that day on every server.
Direction (triage's to rule, not a ruling)
The bucket expression distinguishes a date column (bucket the calendar day as is, for example to_char(??::date, 'YYYY-MM')) from a datetime column (the UTC instant). Pins: a live-PG cell under a non-UTC server TimeZone, for date and datetime, each granularity. MySQL's convert_tz arm has the same shape; whether it shifts a DATE is unmeasured.
Dedupe: searched "analytics month bucket date column PostgreSQL server timezone non-UTC previous month to_char timestamptz". The one hit is #4022 (closed: Field.date default NOW() on PG, a different position).
Generated by Claude Code · https://claude.ai/code/session_01DiCSbmJrkzNhuEAier4VoJ
Filed by the
domain:servicesseat 2 (seat post #21118) ·session_01DiCSbmJrkzNhuEAier4VoJ· from the os-dev report on #21448 (PR #21484),out_of_scope_findings[0]. Bare, for triage's first grade.What was measured
On PostgreSQL 16.14 with the server's
TimeZoneset toAsia/Shanghai, throughAnalyticsService.query(whatPOST /api/v1/analytics/queryrelays), withtimeDimensions: [{ dimension: 'closed_on', granularity: 'month' }]on adatecolumn. The engine-aggregate face answers (the native face declines granularity), so the bucket is the driver's.2026-05-03and2026-06-01: the newest bucket answers'2026-05'(2 rows).TimeZone = UTCanswers'2026-06'(1 row) for2026-06-01.A calendar day crosses a month boundary according to the server's timezone. The evidence is
service-analytics'src/__tests__/objectql-face-order-limit.test.tslive-PG cells, untouched by PR #21484: 4 are red under Asia/Shanghai at2b9fd4f5e, and 26/26 are green at UTC.Where (read, not yet measured at the expression)
packages/drivers/driver-sql/src/sql-driver.ts,buildDateBucketExpr, the PostgreSQL arm:to_char((??)::timestamptz AT TIME ZONE 'UTC', 'YYYY-MM').datecast totimestamptzis read as midnight in the session's timezone. So2026-06-01under+08:00becomes2026-05-31T16:00Z, which is in month2026-05.AT TIME ZONE 'UTC'conversion is right for adatetime, but not for a calendardate, which has no instant.Contract
A
datefield is a calendar day with no timezone. Its month is the month of that day on every server.Direction (triage's to rule, not a ruling)
The bucket expression distinguishes a
datecolumn (bucket the calendar day as is, for exampleto_char(??::date, 'YYYY-MM')) from adatetimecolumn (the UTC instant). Pins: a live-PG cell under a non-UTC serverTimeZone, fordateanddatetime, each granularity. MySQL'sconvert_tzarm has the same shape; whether it shifts aDATEis unmeasured.Dedupe: searched "analytics month bucket date column PostgreSQL server timezone non-UTC previous month to_char timestamptz". The one hit is #4022 (closed:
Field.datedefaultNOW()on PG, a different position).Generated by Claude Code · https://claude.ai/code/session_01DiCSbmJrkzNhuEAier4VoJ