You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET 7 TimeOnly与Postgres无时区时间类型兼容问题排查

问题根源与解决方案

问题根源

PostgreSQL的time类型允许24:00:00作为合法值,用来表示一天的结束(等价于次日的00:00:00),但.NET的TimeOnly类型最大值为23:59:59.9999999。当EF Core/Npgsql尝试将数据库中的24:00:00映射到TimeOnly属性时,就会触发ArgumentOutOfRangeException。

你遇到的毫秒相关现象是因为:当时间为24:00:00.000时,PostgreSQL会省略毫秒显示为24:00:00,此时映射直接触发异常;而如果是24:00:00.001这类带非零毫秒的值,Npgsql会自动将其转换为次日的00:00:00.001,刚好符合TimeOnly的范围,所以不会报错——但这只是巧合,并非根本解决方式。


处理方案

方案1:数据库层面修正数据

直接将数据库中24:00:00的记录替换为00:00:00,两者语义等价(均表示一天结束/次日开始),可彻底避免映射冲突:

UPDATE Shift SET "End" = '00:00:00' WHERE "End" = '24:00:00';

同时修改数据写入逻辑,禁止插入/更新24:00:00,统一用00:00:00替代。

方案2:EF Core自定义映射转换

通过EF Core的值转换器,在映射时自动处理24:00:00的转换:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Shift>()
        .Property(s => s.End)
        .HasConversion(
            // 从TimeOnly转数据库time:将MinValue转为24小时跨度
            time => time == TimeOnly.MinValue ? TimeSpan.FromHours(24) : time.ToTimeSpan(),
            // 从数据库time转TimeOnly:将24小时跨度转为MinValue
            span => span.TotalHours == 24 ? TimeOnly.MinValue : TimeOnly.FromTimeSpan(span)
        );
}

此方案无需修改数据库数据,由ORM层自动兼容两种时间表示。

方案3:改用TimeSpan类型映射

如果业务逻辑允许,可将模型中的TimeOnly替换为TimeSpan:

public partial class Shift
{
    public TimeSpan Start { get; set; }

    public TimeSpan End { get; set; }
}

PostgreSQL的time类型本质是时间跨度,24:00:00对应TimeSpan.FromHours(24),在.NET中是合法值。后续业务中如需TimeOnly,可手动转换(注意将TimeSpan.FromHours(24)转为TimeOnly.MinValue)。

内容的提问来源于stack exchange,提问作者Jamie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 07:33:18