datetime不可变,所有操作返回新实例;adddays()等方法不修改原值,需赋值才生效;解析用tryparse()避免异常;存储用utcnow而非now;格式化注意性能与文化影响。

DateTime 不是可变对象,所有“修改”操作都返回新实例——这是 90% 时间相关 bug 的根源。
为什么 AddDays() 看起来没生效?
因为 DateTime 是值类型且不可变。AddDays(1) 从不改动原变量,只返回一个新 DateTime 实例。
- 错误写法:
dt.AddDays(1);—— 返回值被丢弃,dt完全没变 - 正确写法:
dt = dt.AddDays(1); - 链式调用安全:
var result = dt.AddDays(1).AddHours(2).AddMinutes(30); - 循环中尤其容易踩坑:反复写
dt.AddDays(1)却没赋值,结果每次都在原始时间上加,不是累加
解析字符串该用 Parse() 还是 TryParse()?
生产环境几乎必须用 TryParse() 或 TryParseExact();Parse() 在输入非法时直接抛 FormatException,服务端崩得毫无预警。
-
DateTime.TryParse("2026-05-10", out var d)—— 返回bool,失败不抛异常 - 需要固定格式(如日志文件名里的
"20260510"):用DateTime.TryParseExact("20260510", "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None, out d) - 别传
null当IFormatProvider,线程文化可能随请求变化;服务端一律显式传CultureInfo.InvariantCulture
DateTime.Now 和 DateTime.UtcNow 怎么选?
存储、计算、跨时区逻辑一律用 UtcNow;仅在展示给用户时转本地时间。
-
DateTime.Now受系统时区和夏令时影响,同一时刻在不同时区机器上值不同 -
DateTime.UtcNow是稳定的时间源,适合数据库写入、缓存过期、定时任务触发点 - 错误做法:用
Now存数据库,再用ToUniversalTime()转换——如果原始Kind是Unspecified,转换行为未定义 - 保险做法:创建时就明确
Kind,或直接用DateTimeOffset替代
格式化和性能要注意什么?
ToString() 和 DateTime.Now 看似轻量,高频场景下会暴露隐性开销。
-
DateTime.Now每次调用都触发系统调用,循环里别反复取;高频日志建议一次取值复用 -
ToString("yyyy-MM-dd HH:mm:ss")比拼接字符串快,但带文化信息(如ToString("D"))会触发线程文化查找,慢数倍 -
DateTime.Parse()内部做文化感知解析,比TryParseExact()慢且行为不稳定,别在性能敏感路径用 - 真正要警惕的不是
DateTime本身,而是它常和字符串操作耦合(如日志拼接),ToString()分配的临时字符串才是 GC 压力源
最常被忽略的两个点:一是把 DateTime 当“带时区的时间点”用,却不管 Kind 属性;二是把它当可变对象,忘了所有方法都返回新实例。这两处出错,调试时往往要花双倍时间才定位到。










