从6.0版本开始,Npgsql如果试图保存具有非零偏移量的DateTimeOffset字段,则会抛出异常.

例如:

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时,Postgr似乎在时区中工作得很好.

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)

推荐答案

这在Npgsql个文档中有介绍:

一个常见的错误是用户认为PostgreSQL timestamp with time zone类型在数据库中存储时区.事实并非如此:只存储UTC时间戳.没有一个PostgreSQL类型既存储日期/时间又存储时区,类似于.NET DateTimeOffset.To store a timezone in the database, add a separate text column containing the timezone ID.

PostgreSQL个文档中:

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

当输出带有时区值的时间戳时,它总是从UTC转换为当前时区,并显示为该时区的本地时间.要查看另一个时区的时间,可以更改时区或使用AT TIME ZONE struct (参见第9.9.4节).

我们不建议使用type time with time zone(尽管PostgreSQL支持用于遗留应用程序和符合SQL标准).PostgreSQL假设任何只包含日期或时间的类型的本地时区.

可以观察到:

select *
from test_timestamptz;

这给了我:

some_field
2024-04-10 15:00:00.000000 +00:00
2024-04-10 15:00:00.000000 +00:00
2024-04-10 15:00:00.000000 +00:00

因此,Npgsql自6.0以来引入的行为旨在避免潜在的混淆.

如果需要6.0之前的行为,则无需自定义转换器-您可以使用switch 启用传统驱动程序行为:

AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);

Csharp相关问答推荐

当打印一行x个项目时,如何打印最后一行项目?

FileStream. FlushAsync()是否确保文件被写入磁盘?

为什么总输出就像12.3没有一分一样?

将现有字段映射到EFCore中的复杂类型

如何解决提交按钮后 Select 选项错误空参照异常

Azure函数中实体框架核心的依赖注入

TeamsBot SendActivityActivityTypes与ActivityTypes同步.键入不再起作用

反序列化私有成员

如何通过寻找向量长度来优化两个循环?

如何在C#中创建VS代码中的控制台应用程序时自动生成Main方法

该函数不能检测两条曲线的交点

如何将FindAll()与Nuget包Interop.UIAutomationClient一起使用

按需无缝转码单个HLS数据段

在.NET8中如何反序列化为私有字段?

C# Winforms:从对象树到TreeView的递归转换重复条目

NETSDK1201:对于面向.NET 8.0和更高版本的项目,默认情况下,指定RUNTIME标识符将不再生成自包含的应用程序

Azure队列触发器未使用隔离的工作进程执行

如何对构建在Clean架构和CQRS之上的控制器进行单元测试?

CsvHelper在第二次迭代时抛出System.ObjectDisposedException

将ValueTask发送给调用者