Conversation
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.
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
This PR reads a
DateTimeorDateTimeOffsetfilter value without an offset as UTC. This is a breaking change. It is one of the six breaking changes that were removed from #114 so that main stays compatible with v1.14.2.Status: for later consideration. Do not merge this PR now. The captain decides on each breaking change separately. If this change is accepted, it needs a major version, or the opt-in setting below.
Old behavior (v1.14.2 and main)
DateTimeStyles.AssumeLocal).DateTimevalue getsDateTimeKind.Local.AdjustToUniversalwithoutAssumeUniversal. Thus they use a different rule from the scalar value.DateTimevalue is a query parameter (since perf: parameterize filter values and cache parsers and regexes #110). Npgsql rejects aLocalDateTimefor atimestamp with time zonecolumn. As a result, the filter throws on Postgres in every server time zone.New behavior
AssumeUniversal | AdjustToUniversal).DateTimevalue with an offset to UTC.ParseDateTimeandParseDateTimeOffset.Example
x => (x.SpecificDateTime == new DateTime(637922304030000000, Local)). On a server in UTC+3, this value is 2022-06-30T21:00:03Z.x => (x.SpecificDateTime == new DateTime(637922304030000000, Utc)). The instant is 2022-07-01T00:00:03Z on every server.Proof from the
verify-querykitharness, on a machine in UTC+3. Pancakes hasCreatedAt2024-01-15 08:00 UTC.CreatedAt == 2024-01-15T08:00:00DateTimeequality ignores theKind)CreatedAt == 2024-01-15T08:00:00ArgumentException: "Cannot write DateTime with Kind=Local to PostgreSQL type 'timestamp with time zone'"@Value='2024-01-15T08:00:00.0000000Z'CreatedAt == 2024-01-15T10:00:00+02:00@Value='2024-01-15T08:00:00.0000000Z'Justification
A filter string comes from a client, and the client does not know the time zone of the server. In v1.14.2, the same filter matches different rows on servers in different time zones. The results can also change after a deploy to a new region, or after a change to the container time zone.
Most APIs store and compare instants in UTC, and Npgsql requires UTC for
timestamp with time zone. When the parser reads a value without an offset as UTC, the result depends only on the filter string. The change also removes the difference between scalar values and list values, which is a fault on its own.Migration and opt-in alternative
2022-07-01T00:00:03+03:00.DateTimeKindForValuesWithoutOffset. The setting can keep the v1.14.2 default until the next major version. This PR does not add the setting.Tests
FilterParserTestsnow expectsUtcin 3 places, notLocal.date_time_without_offset_is_utcanddate_time_list_value_matches_scalar_value.date_time_without_offset_is_utc.dotnet test: 356 unit tests and 276 integration tests pass.