DateTime相减最安全常用,结果为TimeSpan;务必统一Kind并区分Days与TotalDays,格式化优先用"g"或"G"。

直接用 DateTime 相减得到 TimeSpan,是最安全、最常用的方式;别绕路去手动算 ticks 或用 DateAndTime.DateDiff(后者属 VB 兼容层,不推荐新项目)。
DateTime 相减 vs Subtract() 方法
两者完全等价:later - earlier 和 later.Subtract(earlier) 都返回同一个 TimeSpan 实例。编译器会把减号转成 Subtract 调用,所以选哪个纯看可读性——多数人更习惯减号写法。
- 如果逻辑上强调“从 A 中减去 B”,用
.Subtract()更自解释 - 若只是表达“时间差”,
dt2 - dt1更简洁自然 - 注意结果可能为负:比如
new DateTime(2020, 1, 1) - new DateTime(2025, 1, 1)得到的是负的TimeSpan,需要Math.Abs()才能取绝对间隔 - 务必确认两个
DateTime的Kind一致(都是DateTimeKind.Utc或都是DateTimeKind.Local),否则跨时区比较会出偏差
Days 和 TotalDays 别混用
TimeSpan.Days 是“整数天部分”,只取截断后的天数(-365 到 365),而 TimeSpan.TotalDays 是总天数(含小数),精度高且无范围限制。
- 判断是否超 7 天?用
ts.TotalDays > 7,不是ts.Days > 7 - 显示“3 天 5 小时”?才用
ts.Days搭配ts.Hours(注意ts.Hours是剩余小时,0–23) -
ts.TotalHours、ts.TotalMinutes同理,和.Hours、.Minutes完全不同 - 要存数据库或做数值比较,一律优先用
TotalXxx系列属性
ToString 格式化容易崩,改用 "g" 或 "G"
自定义格式如 "d\.hh\:mm\:ss" 看似直观,但里面的 d 表示“天数字段”,默认只接受 0–365,超出就抛 FormatException——比如 400 天的间隔直接失败。
- 安全做法是用标准格式符:
ts.ToString("g")(简洁,如"400.12:30:45")或"G"(带毫秒,如"400.12:30:45.1230000") - 如果必须自定义,改用
ts.TotalDays等数值 + 字符串拼接,避开ToString的 d 限制 - 跨日计算前建议先归一化日期:
(dt2.Date - dt1.Date).Days,避免时分秒干扰
从 DateTimeOffset 或数据库来的时间要先统一 Kind
很多真实场景中,时间来自 API、SQL Server 或 JSON,类型可能是 DateTimeOffset 或带时区信息的字符串。直接转 DateTime 可能丢失偏移或误转本地时间。
- 从
DateTimeOffset来?用dto.UtcDateTime或dto.LocalDateTime显式提取,再参与减法 - 从数据库读出的
datetime2默认是Unspecified,建议在 ORM 层或查询后统一设为DateTimeKind.Utc或Local,例如:dt.ToUniversalTime().AddTicks(0)再设Kind - 别依赖
DateTime.SpecifyKind生硬指定——如果原始值其实是 UTC 却被标成 Local,后续转换全错
最常被忽略的是 Kind 不一致和 Days/TotalDays 混用,这两个点一旦出错,结果偏差可能毫无征兆,尤其在跨月、跨年或跨时区场景下。写完记得用边界值(如相隔 0 秒、366 天、UTC vs Local 时间对)快速验证一下。


















