fix(parser)!: read date values without an offset as utc, with DateTimeKindForValuesWithoutOffset - #116
Merged
Conversation
pdevito3
force-pushed
the
fm/qk-breaking-prs-114
branch
3 times, most recently
from
October 1, 2026 21:24
20a5851 to
85c0b76
Compare
pdevito3
force-pushed
the
fm/qk-breaking-prs-114
branch
from
October 3, 2026 11:55
85c0b76 to
8b550ea
Compare
pdevito3
force-pushed
the
fm/qk-breaking-prs-114
branch
from
October 3, 2026 11:58
8b550ea to
31588be
Compare
A DateTime or DateTimeOffset value without an offset was read in the time zone of the server. The same filter matched different rows on servers in different zones. List values and custom operation values also used a different rule from scalar values. A value without an offset is now read as UTC. A DateTime value with an offset is converted to UTC. The scalar, list, and custom operation paths use the same two helpers. BREAKING CHANGE: a DateTime or DateTimeOffset filter value without an offset is read as UTC, not in the time zone of the server. A scalar DateTime value now has DateTimeKind.Utc, not DateTimeKind.Local. To keep a local time, send the offset in the value, for example 2022-07-01T00:00:03+03:00.
…thout time zone columns
pdevito3
force-pushed
the
fm/qk-breaking-prs-114
branch
from
October 3, 2026 12:00
31588be to
be6786c
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A
DateTimeorDateTimeOffsetfilter value without an offset is now UTC by default. The new settingDateTimeKindForValuesWithoutOffset(defaultDateTimeKind.Utc) selects a different kind.DateTimeKindForValuesWithoutOffset2024-01-15T08:00:00becomesUtc(default)Kind=Utctimestamp with time zone(timestamptz) on Npgsql, and most APIsUnspecifiedKind=Unspecifiedtimestamp without time zoneon NpgsqlLocalKind=LocalThe setting is on
QueryKitSettings,QueryKitConfiguration, andIQueryKitFilterBehavior(a default interface member that returnsUtc, so existing implementers still compile). The parser sets it for one parse on the current thread and restores it after the parse.Breaking change
Npgsql needs a
UtcDateTimefor atimestamptzcolumn. It rejects aUtcDateTimefor atimestamp without time zonecolumn.timestamptzcolumns: a filter value without an offset threw on Postgres in v1.14.2. Now it works. No action is necessary.timestamp without time zonecolumns: a filter value without an offset now throws "'timestamp without time zone' literal cannot be generated for a UTC DateTime". To correct this, set the kind toUnspecified:Local.timestamptzcolumn and atimestamp without time zonecolumn on Npgsql.The README has a new section, "Date and Time Values Without an Offset".
Evidence
verify-querykitharness, Postgres, machine time zone EET (UTC+2).CreatedAtistimestamptz.LocalCreatedAtistimestamp without time zone. Pancakes has 2024-01-15 08:00.CreatedAt == "2024-01-15T08:00:00"ArgumentException: "a UTC DateTime is required"CreatedAt ^^ [2024-01-15T08:00:00]ArgumentException: "a UTC DateTime is required"LocalCreatedAt == "2024-01-15T08:00:00"LocalCreatedAt == "2024-01-15T08:00:00"unspecified-datetime, literal and parameterizedTIMESTAMP '2024-01-15T08:00:00'LocalCreatedAt ^^ [2024-01-15T10:00:00+02:00]unspecified-datetimeTests:
DateTimeKindForValuesWithoutOffsetTests: theUtc,Unspecified, andLocalkinds for the scalar, quoted, list, quoted-list, and custom operation forms, with and without parameters. Also values with an offset,DateTimeOffset, the restore after the parse, and an interface implementer without the setting.timestamptz_value_without_offset_is_utc_by_default: 24 cases onSpecificDateTimeandSpecificDate.timestamp_without_time_zone_value_matches_with_unspecified_kind: 12 cases on the newLocalDateTimecolumn (migrationAddPersonLocalDateTime).dotnet testafter the rebase on v2: 564 unit tests and 357 integration tests pass. The build with-warnaserrorhas 0 warnings. A nullable warning from fix(filter)!: resolve query names in the grammar again #154 inAliasCultureTestsis corrected here.Merge Danger
Door: two-way
The change is code only. The new test column is in the test web project, not in the library.
Blast Radius: consumers
Each consumer that filters a
timestamp without time zonecolumn on Npgsql with a value without an offset must setUnspecified. On a server that is not on UTC, a consumer that depended on the server time zone gets different rows. That consumer must setLocal.