dateonly 是 .net 6+ 引入的独立、不可变、无时区纯日期类型,内部存储天数整型,避免 datetime 带来的时间语义和时区歧义;需显式指定文化进行解析,ef core 7+ 原生支持映射到数据库 date 类型。

DateOnly 不是 DateTime 的简化版,它是一个独立、不可变、无时区的纯日期值类型,.NET 6+ 才可用,且不能用于 .NET Framework。
为什么不能直接用 DateTime 表示“只有日期”
DateTime 带时间部分和 DateTimeKind,哪怕你设成 new DateTime(2023, 5, 1, 0, 0, 0),它仍可能在序列化、数据库映射或跨时区传递时被意外解释为前一天或后一天(比如 UTC 转本地时区)。而 DateOnly 从设计上就剥离了所有时间语义——它只存一个从公元元年算起的天数(内部是 int),不参与任何时区计算,也不隐含“当天零点”这个时间点。
常见错误现象:
- 用
DateTime.Date属性取日期后传给前端或数据库,结果 JSON 序列化仍带"00:00:00",API 消费方误以为需要处理时间 - EF Core 映射
DateTime到 SQL Server 的date类型时,虽能工作,但模型语义模糊,校验逻辑容易漏掉时间部分的非法值(如23:59:59) - 做日期比较时,用
dt1.Date == dt2.Date看似可行,但每次调用都新建DateTime实例,有轻微开销;而date1 == date2是纯整数比对
DateOnly 构造与解析的坑:CultureInfo 必须显式传
DateOnly 的 Parse 和 TryParse 方法默认依赖当前线程的 CultureInfo,但 Web API 或后台服务中线程文化常是 en-US 或 invariant,导致 "28/09/1984" 这类日/月/年格式直接失败。
正确做法:
- 始终显式传入
IFormatProvider,例如new CultureInfo("en-GB")或CultureInfo.InvariantCulture - 避免用
Parse(string)重载,改用TryParse(string, provider, out DateOnly),防止异常中断流程 - 若输入格式固定(如
"yyyy-MM-dd"),优先用TryParseExact,性能更高且不依赖文化设置
示例:
if (DateOnly.TryParseExact("2026-05-14", "yyyy-MM-dd", null, DateTimeStyles.None, out var d))
{
Console.WriteLine(d); // 输出:2026-05-14
}
和 DateTime 互转时要注意“时间丢失”和“时区陷阱”
DateOnly.ToDateTime(TimeOnly) 和 DateOnly.ToDateTime() 看似方便,但它们会把日期“补全”为一个 DateTime,而这个补全行为有隐含规则:
-
date.ToDateTime()默认补时间为00:00:00,且Kind为Unspecified—— 后续若参与时区转换(如ToUniversalTime()),结果可能出错 -
date.ToDateTime(new TimeOnly(15, 30))生成的DateTime仍是Unspecified,不是Local或Utc - 反向转换
DateOnly.FromDateTime(dt)是安全的,但会丢弃时间部分;若dt.Kind == DateTimeKind.Utc,它不会自动转成本地日期,而是直接截断 —— 即new DateTime(2026, 5, 14, 23, 0, 0, DateTimeKind.Utc)转成DateOnly仍是2026-05-14,不是2026-05-15
所以:只要业务逻辑里真只需要日期,就全程用 DateOnly;一旦涉及“某天某个时刻”,再引入 TimeOnly 或 DateTimeOffset,不要混用。
EF Core 中映射 DateOnly 到数据库的注意事项
EF Core 7+ 原生支持 DateOnly 映射到 SQL Server date、PostgreSQL date、SQLite TEXT(ISO 格式)等类型。但有两个关键点容易被忽略:
- 必须确保 DbContext 使用的 EF Core 版本 ≥ 7.0;EF Core 6 只支持手动配置 ValueConverter,否则会报
System.InvalidOperationException: The property 'X.Date' is of type 'DateOnly' which is not supported by the current database provider - SQL Server 的
date类型范围是0001-01-01到9999-12-31,和DateOnly.MinValue/.MaxValue完全一致;但如果你用的是旧版 SQL Server(如 2005),它不支持date类型,只能退回到datetime,此时建议坚持用DateTime避免兼容性问题 - 迁移生成的建表语句中,字段类型应为
date,而不是datetime2;如果发现仍是后者,检查是否忘了在OnModelCreating中为该属性调用HasColumnType("date")
最简映射写法(EF Core 7+):
modelBuilder.Entity<order>()
.Property(e => e.OrderDate)
.HasColumnType("date");</order>
复杂点在于——它不支持 HasDefaultValueSql("GETDATE()") 这类数据库函数,因为 GETDATE() 返回的是 datetime,需改用 CAST(GETDATE() AS date) 或直接在 C# 层赋默认值 DateOnly.FromDateTime(DateTime.Today)。










