直接用 DateTime 相减得 TimeSpan 是最可靠方式,避免手动计算闰年、时区等问题;需确保 Kind 一致(推荐全转 Utc),并注意 TimeSpan.Days 与 TotalDays 的本质区别。

用 DateTime.Subtract() 得到 TimeSpan 是最直接的方式
别绕弯子——两个 DateTime 相减,C# 会自动返回 TimeSpan,这是设计好的行为,不是“技巧”。TimeSpan 本身不表示“从哪到哪”,只表示一段持续时间,所以它天然适合算差值。
常见错误是手动拆解年月日去计算天数,结果漏掉闰年、时区、夏令时,甚至 DateTime.Kind 不一致导致意外偏移。直接相减能避开所有这些。
-
DateTime必须是同一种Kind(Unspecified、Local或Utc),否则减法虽能运行,但语义模糊;推荐统一转成Utc再算 - 如果其中一个值是
null(比如来自数据库的可空字段),要先判空,否则抛NullReferenceException -
TimeSpan.TotalDays返回double,含小数;要整数天数就用TimeSpan.Days(只取整数部分,截断不四舍五入)
DateTime start = new(2023, 1, 15, 14, 30, 0, DateTimeKind.Utc); DateTime end = new(2023, 2, 20, 9, 15, 0, DateTimeKind.Utc); TimeSpan diff = end.Subtract(start); // 或直接写 end - start Console.WriteLine(diff.TotalDays); // 35.822916666666664 Console.WriteLine(diff.Days); // 35(只取整天数)
TimeSpan.Days 和 TimeSpan.TotalDays 差在哪
这是最容易混淆的点:两者都“算天数”,但含义完全不同。
-
TimeSpan.Days是TimeSpan的“日字段”值,范围是 -24 到 +24,只反映超出小时的部分(比如 36 小时 →Days = 1,Hours = 12) -
TimeSpan.TotalDays才是你真正想要的“总天数”,是把整个时间跨度换算成天的小数值(36 小时 = 1.5 天) - 如果跨度超过 24 天,
Days会“归零重算”,完全不可靠;必须用TotalDays或Math.Floor(ts.TotalDays)获取完整天数
跨时区计算前必须先统一 DateTimeKind
用户常以为 DateTime 存的是“绝对时间”,其实不是——它的 Kind 属性决定解释方式。本地时间减 UTC 时间,差值可能多出或少掉 8 小时,且随夏令时浮动。
- 用
DateTime.SpecifyKind(dt, DateTimeKind.Utc)强制指定类型,仅当原始值确实代表该时区时间时才安全 - 更稳妥的做法是:用
dt.ToUniversalTime()转换(注意:对Unspecified值,.NET 默认按本地时区处理,可能出错) - 如果数据来自 API 或数据库,优先存 UTC,读取后直接用,避免反复转换
// 错误示范:混合 Kind DateTime local = DateTime.Now; // Local DateTime utc = DateTime.UtcNow; // Utc TimeSpan bad = utc - local; // 结果取决于本地时区偏移,不稳定 <p>// 正确做法:全转 UTC TimeSpan good = utc - local.ToUniversalTime();
需要精确到“自然日”(如日历上数格子)就得用 Calendar
前面所有方法算的都是“连续 24 小时为一天”的物理天数。但如果你要的是“从 3 月 31 日到 4 月 2 日是 3 天”,即日历上的日期计数,TimeSpan 就不够用了——它不管月份长度、闰年、起止时间点。
-
TimeSpan对 3 月 31 日 23:59 到 4 月 1 日 00:01,只算 0.001 天;但人眼日历是跨了两天 - 这时得用
Calendar.GetYear()/GetMonth()/GetDayOfMonth()手动比对,或借助NodaTime库的Period.Between() - .NET 原生没提供“日历天数”API,硬写容易漏边界(比如跨年、跨月),建议明确需求再决定是否引入第三方
实际项目里,80% 的“天数差”需求其实只要 (end - start).TotalDays 就够了。真要日历逻辑,先确认业务方说的“差几天”到底指什么——是倒计时?工单处理时长?还是排班表跨度?不同场景,解法差很远。










