timespan不是时间点,不能当datetime用;计算差值须统一时区;格式化禁用"d";subtract返回timespan而非datetime;totaldays等总值属性才可用于运算和判断。

直接说结论:TimeSpan 不是时间点,不能当 DateTime 用;计算差值必须统一时区;格式化别乱用 "d",它对 TimeSpan 完全无效。
DateTime.Subtract() 返回 TimeSpan,不是 DateTime
这是最常卡住新手的第一步。写 dt1.Subtract(dt2) 或 dt1 - dt2,结果类型是 TimeSpan,不是 DateTime。强行赋值给 DateTime 变量会编译失败。
- 错误写法:
DateTime diff = dt1 - dt2;→ 编译报错 CS0029 - 正确写法:
TimeSpan diff = dt1 - dt2; - 本质区别:
DateTime是“某个时刻”,TimeSpan是“一段长度”,比如“3天2小时”——它没有起点,只有跨度
TimeSpan.Days 和 TimeSpan.TotalDays 差得离谱
想算“总共多少天”?用 TotalDays。用 Days 只会拿到整数天部分,丢掉所有小时/分钟/秒,极易导致逻辑错误。
-
TimeSpan ts = new TimeSpan(36, 0, 0); // 36 小时
→ts.Days == 1,但ts.TotalDays == 1.5 - 判断是否超 72 小时:
if (ts.TotalHours > 72)✅,if (ts.Hours > 72)❌(Hours最大只到 23) - 参与数学运算(比如除以 2、四舍五入)必须用
TotalXxx系列:TotalSeconds、TotalMinutes、TotalMilliseconds
TimeSpan.ToString() 格式字符串只有 "c"、"g"、"G" 有效
ToString("d") 是 DateTime 的短日期格式,对 TimeSpan 完全没意义,调用会抛 FormatException。标准格式只有三个:
-
"c":规范格式,文化无关,如1.12:24:02(1天12小时24分02秒),推荐用于日志或 API 输出 -
"g":紧凑格式,省略前导零和无意义字段,如1:2:3或1.2:3:4.5 -
"G":长格式,带全部字段和小数位,如1.12:24:02.0000000 - 自定义格式必须转义特殊字符:
ts.ToString(@"dd\.hh\:mm\:ss")才能得到01.12:24:02
跨时区计算必须统一 Kind,否则差几小时都算不准
服务器上混用 DateTime.Now(Local)和 DateTime.UtcNow(UTC),相减结果可能偏差 8 小时(比如东八区),而且这个偏差在不同机器上还不一致。
- 检查 Kind:
dt.Kind必须全是DateTimeKind.Utc或全是DateTimeKind.Local - 入库和计算一律用 UTC:
DateTime.UtcNow存库,计算也用它 - 显示时再转本地:
dt.ToLocalTime()或TimeZoneInfo.ConvertTimeFromUtc() - 负的
TimeSpan要小心:ts.Days是负值,但ts.Hours始终 ≥ 0 → 想安全取绝对天数,用ts.Duration().Days
真正麻烦的是负值处理和时区对齐——这两处不显眼,但一出错就是线上 bug,而且很难复现。










