需要深入了解NpgSQL DateTimeOffset处理

问题描述 投票:0回答:1

自版本 6.0 起,如果尝试保存具有非零偏移量的

DateTimeOffset
字段,NpgSQL 会引发异常。 例如:

public class User
{
  public Guid Id { get; private set; }
  public DateTimeOffset? LockedUntil { get; private set; }
  public void Lock()
  {
    LockedUntil = DateTimeOffset.Now.Add(TimeSpan.FromDays(1));
  }
}
// ...
builder.Property(u => u.Id);
builder.Property(u => u.Login);
// ...
var user = dbContext.Users.First(u => u.Id == someId);
user.Lock();
dbContext.SaveChanges();

这会导致异常:

System.ArgumentException: ... only offset 0 (UTC) is supported
我很好奇为什么? 我的意思是我确实知道这是有记录的行为。 同时,当类型为
timestamptz
时,Postgres 似乎可以完美地处理时区。

SELECT '2024-04-10T15:00:00.000Z'::timestamptz = '2024-04-10T13:00:00.000-02:00'::timestamptz;
--> true

create table test_timestamptz(some_field timestamptz);
INSERT INTO test_timestamptz(some_field)
VALUES ('2024-04-10T15:00:00.000Z'), ('2024-04-10T13:00:00.000-02:00'), ('2024-04-10T20:00:00.000+05:00');

SELECT COUNT(*) FROM test_timestamptz WHERE some_field = '2024-04-10T15:00:00.000Z';
--> 3
SELECT COUNT(*) FROM test_timestamptz WHERE some_field = '2024-04-10T16:00:00.000+01:00';
--> 3

在我的项目中,我希望能够使用具有不同时区的时间值(特别是比较它们)。

DateTimeOffset
给了我这个并隐藏了 TZ 数学。 毕竟我最终得到了这种转换器:

public class DateTimeOffsetToDateTimeConverter : ValueConverter<DateTimeOffset, DateTime>
{
  public DateTimeOffsetToDateTimeConverter() : base(
    v => v.UtcDateTime,
    v => new DateTimeOffset(v))
  {
  }
}

这可能是某种技术限制吗?或者我误解了什么(C# 不是我的主要 PL)

c# .net postgresql entity-framework npgsql
1个回答
0
投票

这在 Npgsql 文档中有介绍:

用户常见的错误是认为 PostgreSQL

timestamp with time zone
类型将时区存储在数据库中。情况并非如此:仅存储 UTC 时间戳。没有一个 PostgreSQL 类型可以像 .NET DateTimeOffset 那样同时存储日期/时间和时区。要在数据库中存储时区,请添加包含时区 ID 的单独文本列。

来自 PostgreSQL 文档:

对于

timestamp with time zone
,内部存储的值始终采用 UTC(通用协调时间,传统上称为格林威治标准时间,GMT)。指定了显式时区的输入值将使用该时区的适当偏移量转换为 UTC。如果输入字符串中未指定时区,则假定其处于系统 TimeZone 参数指示的时区,并使用时区的偏移量将其转换为 UTC。

当输出带有时区值的时间戳时,它始终从 UTC 转换为当前时区,并显示为该区域的本地时间。要查看另一个时区的时间,请更改时区或使用 AT TIME ZONE 构造(请参阅第 9.9.4 节)。

还有

我们不建议使用

type time with time zone
(尽管 PostgreSQL 支持旧版应用程序并符合 SQL 标准)。对于仅包含日期或时间的任何类型,PostgreSQL 都会假定您的本地时区。

因此 Npgsql 从 6.0 开始引入的这种行为是为了避免潜在的混乱。如果您愿意,可以使用开关启用旧版驱动程序行为:

AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
© www.soinside.com 2019 - 2024. All rights reserved.