Preserve named time zones when persisting ZonedDateTime in JSON

Đang mở
#4,036 0 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 1 ngày

Chưa có ai nhận issue này.

Đánh giá

Độ khó
5/5
Thời gian dự kiến
Hơn một tuần
Mức phù hợp với người mới
25/100
Loại issue
Tính năng
Độ rõ ràng
Khá rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
csharp, postgresql
Lĩnh vực
database, databases

Hướng nghiên cứu

Start with TimestampTzZonedDateTimeMapping and the JSON reader/writer used in the repro, then inspect the prior implementation and tests at commit 817b4fff. First decide the persisted representation and compatibility policy, including existing data, query behavior, and precision; then trace JSON projections, predicates, recordset fields, collections, and containment. Done means named zones and offsets round-trip while existing deployments and query semantics are explicitly addressed.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

Description

ZonedDateTime JSON currently stores the local date/time and UTC offset, but not the named time zone. A value in Europe/Berlin is therefore indistinguishable from a value in another zone with the same offset. Deserialization cannot recover the original zone identity or its daylight-saving rules.

Split out of #4021. That issue covers the reader throwing on the existing writer's nonzero-offset output; #4022 fixes that failure without changing the persisted format, returning a fixed-offset zone. This issue covers the separate capability of preserving the original named zone and is not fixed by #4022.

Minimal repro

Run this as a .NET 11 console application referencing src/EFCore.PG.NodaTime/EFCore.PG.NodaTime.csproj from this repository. No PostgreSQL server is required.

using System;
using NodaTime;
using Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal;

var readerWriter = TimestampTzZonedDateTimeMapping.Default.JsonValueReaderWriter!;

foreach (var zoneId in new[] { "Europe/Berlin", "Europe/Paris" })
{
    var value = new LocalDateTime(2018, 4, 20, 10, 31, 33)
        .InZoneLeniently(DateTimeZoneProviders.Tzdb[zoneId]);
    Console.WriteLine(value.Zone.Id);
    Console.WriteLine(readerWriter.ToJsonString(value));
}

Both values produce the same JSON string (the plus sign may be JSON-escaped as \u002B):

Europe/Berlin
"2018-04-20T10:31:33+02"
Europe/Paris
"2018-04-20T10:31:33+02"

After the compatible reader fix in #4022, this representation can be read as a fixed-offset zone, but neither original zone ID can be recovered from it.

Desired behavior

Provide a way to persist and recover the original named zone for ZonedDateTime JSON values. Retain the explicit offset so ambiguous local times during a daylight-saving overlap can be disambiguated. This concerns JSON document round-tripping; PostgreSQL timestamptz columns do not preserve named zones.

Proposed approach and compatibility decisions

An earlier implementation in #4022 used a JSON-specific NodaTime pattern, ZonedDateTimePattern.ExtendedFormatOnlyIso.WithZoneProvider(DateTimeZoneProviders.Tzdb), producing text such as:

2018-04-20T10:31:33 Europe/Berlin (+02)

It was removed in commit 8681ae17 because it was not a backward-compatible bug fix. The proposed representation remains a candidate, not an approved design:

  • Older providers cannot read the new output, even for UTC. A fallback in the new reader does not make mixed-version deployments or rollbacks safe. Decide whether this should be opt-in or a documented breaking change, and define existing-data/migration behavior.
  • The zone-bearing string is not valid PostgreSQL timestamptz input. EF scalar JSON projections/predicates, typed json[b]_to_recordset fields, primitive collections, and representation-sensitive containment need deliberate query semantics. EF-only conversions would not repair application-written SQL, views, or indexes that cast the JSON text directly.
  • Keep SQL and embedded PostgreSQL literal formatting separate from JSON representation changes.
  • The historical pattern also expanded precision from six fractional digits to nanoseconds. That expansion is not required for named-zone persistence and should be decided separately. PostgreSQL rounds to microseconds while Npgsql parameter encoding truncates toward the PostgreSQL epoch; blindly expanding precision can make equality predicates miss saved values.
  • Clearly distinguish full-document named-zone round-tripping from scalar timestamptz query results, which retain instant/UTC semantics rather than named-zone identity.
Prior implementation

The complete reverted implementation and its tests can be inspected at 817b4fff. It added mapping-specific JSON extraction conversion, a query postprocessor, a containment optimization exclusion, and precision normalization. Those changes are historical reference, not a requirement to reintroduce that architecture. The follow-up should choose the simplest justified representation and compatibility policy before implementation.

Ngôn ngữ chính
C#
Star
1.8k
Fork
268
Merge trung bình
2 giờ 35 phút
Pull request đã merge (30 ngày)
136

Chuẩn bị môi trường

Dự án này không cung cấp dev container, Dockerfile hay hướng dẫn đóng góp, nên bạn cần tự thiết lập môi trường: hãy bắt đầu từ README và xem hướng dẫn đóng góp lần đầu của chúng tôi để biết các bước chung.

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của npgsql/efcore.pg

Tất cả issue của npgsql/efcore.pg

Issue tương tự

Thêm issue về C#

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.