datetime.subtract() 直接返回 timespan,应优先使用;注意 .days 与 .totaldays 区别:前者为整数天部分,后者为精确小数天;超时判断用 totalhours 或 duration();高精度测时用 stopwatch;业务日期逻辑需用 nodatime 等专用库。

用 DateTime.Subtract() 得到 TimeSpan 是最直接的方式
两个 DateTime 相减,C# 会自动返回 TimeSpan 对象,里面封装了天、小时、分钟等完整差值信息。这不是“计算技巧”,而是语言内置行为,别绕路去手动拆年月日。
常见错误是拿到 TimeSpan 后只读 .TotalDays 或 .Days,却没注意二者区别:.Days 是总天数取整(忽略小时分钟),.TotalDays 是带小数的精确天数(比如 1.5 表示 36 小时)。
- 要判断是否超过 24 小时 → 用
ts.TotalHours > 24 - 要显示“X 天 Y 小时” → 用
ts.Days和ts.Hours(注意:这两个是“剩余部分”,不是累计) - 跨时区比较前,先统一转成
DateTimeKind.Utc,否则Subtract()可能因本地时区偏移出错
TimeSpan 的 .Days 和 .TotalDays 容易混淆
TimeSpan.Days 返回的是“天字段”的值,范围是 -999 到 +999,只代表差值中完整的天数部分;而 TimeSpan.TotalDays 是把整个时间跨度换算成天(含小数)。比如相差 36 小时,.Days == 1,.TotalDays == 1.5。
典型误用场景:做超时判断时写 if (ts.Days >= 1),结果 23 小时 59 分钟也被放过——因为 .Days 还是 0。
- 判断是否满 1 天 → 用
ts.TotalDays >= 1或ts >= TimeSpan.FromDays(1) - 格式化输出“3天12小时” →
$"{ts.Days}天{ts.Hours}小时"(前提是 ts 非负且不超 int 范围) - 如果 ts 是负的(后一个时间更早),
.Days和.Hours也会是负数,但.Duration()可以取绝对值
需要考虑日期精度时,避免直接用 DateTime.Now
DateTime.Now 的分辨率通常只有 10–15 毫秒,连续两次调用可能返回相同值,导致 Subtract() 出现 0 差值。这不是 bug,是 Windows 系统计时器限制。
如果你在测代码执行耗时、或做高频时间差比对,DateTime 不够用。
- 微秒/毫秒级测量 → 改用
Stopwatch,它基于高精度性能计数器 - 记录业务事件时间戳 → 用
DateTime.UtcNow更稳妥,避免本地时区夏令时跳变影响差值 - 存数据库前若需保留毫秒 → 确保 SQL Server 类型是
datetime2,而不是旧版datetime(后者精度仅 3.33ms)
跨日历或特殊文化场景下,别用 TimeSpan 算“业务天数”
TimeSpan 算的是纯粹的 24 小时周期,但“工作日”“自然月”“农历生日”这些概念无法靠它解决。比如两个日期之间隔了 3 个周末,实际工作日可能只有 5 天。
这时候 TimeSpan 给出的“7天”只是物理时间,和业务逻辑无关。
- 计算工作日 → 手动遍历
DateTime,跳过周六日和节假日;或用第三方库如NodaTime - 按月/年差值 → 用
DateTime.AddMonths()或NodaTime.Period,别用TimeSpan.TotalDays / 30 - 涉及农历、波斯历等 → .NET 基础库不支持,必须引入
NodaTime或专用日历类
时间差值本身很简单,难的是你到底想表达什么——是机器眼里的秒数,还是人眼里的“过了三天”“下周二”,这个意图一旦模糊,后面全错。










