应使用 DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() 获取 Unix 时间戳毫秒值;测代码执行时间必须用 Stopwatch,因其基于高精度性能计数器,不受系统时钟影响,而 DateTime 仅适用于显示或粗略时间点。

用 DateTime.UtcNow 获取当前毫秒数,但别直接当计时器用
它返回的是协调世界时的完整时间点,DateTime.UtcNow.Millisecond 只取当前秒内的毫秒部分(0–999),不是自程序启动或自纪元以来的总毫秒数。真要“当前毫秒数”指 Unix 时间戳毫秒值,得用 DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() —— 这才是你搜“C# 毫秒数”时真正想拿的那个数字。
常见错误现象:DateTime.Now.Millisecond 被当成全局唯一毫秒计数器,结果一秒内多次调用全撞在同一个值上;或者用 DateTime.Now.Ticks / 10000 手动算毫秒,却忽略时区偏移和夏令时扰动。
- 如果目标是生成唯一 ID 或日志时间戳:优先用
DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() - 如果只是想显示“当前秒内过了多少毫秒”:用
DateTime.UtcNow.Millisecond就够,但别指望它递增 -
DateTime.Now在 Windows 上有约 15ms 系统时钟粒度,实际精度远低于毫秒,别对它做减法测间隔
精确测量代码执行时长必须用 Stopwatch,不是 DateTime
DateTime 类型靠系统时钟,受 NTP 同步、手动调时、睡眠唤醒影响,两次取值相减可能倒退或跳变。而 Stopwatch 底层调用高精度性能计数器(QueryPerformanceCounter),不受系统时间干扰,是 .NET 唯一推荐的纳秒级计时方案。
使用场景:压测单个方法耗时、评估算法性能、诊断 I/O 延迟瓶颈。
- 初始化后立即
Start(),结束前调Stop(),再读ElapsedMilliseconds或ElapsedTicks - 重复测量时,用
Restart()比Stop(); Start();更安全,避免中间状态遗漏 - 注意
Stopwatch.IsHighResolution返回false时(极少见),说明系统不支持高精度计数器,此时降级为DateTime级别精度
var sw = Stopwatch.StartNew();
DoWork();
sw.Stop();
Console.WriteLine($"耗时:{sw.ElapsedMilliseconds} ms");
Stopwatch.GetTimestamp() 是底层 Tick,别自己除 10000
Stopwatch.GetTimestamp() 返回的是硬件计数器原始值(类似 CPU cycle),单位不是微秒也不是毫秒。它的换算依赖 Stopwatch.Frequency:真实纳秒 = (timestamp * 1_000_000_000L) / Stopwatch.Frequency。直接用 ElapsedMilliseconds 更稳,除非你在写底层性能库需要规避 TimeSpan 构造开销。
容易踩的坑:看到文档里说 “1 tick = 100ns”,就以为 GetTimestamp() / 10000 就是毫秒——错。这个 100ns 是 .NET 的逻辑抽象,实际硬件频率由 CPU 和主板决定,Stopwatch.Frequency 才是唯一可信换算依据。
- 不要手算毫秒:用
sw.ElapsedMilliseconds或sw.Elapsed.TotalMilliseconds - 需要更高精度(比如微秒):用
sw.ElapsedTicks配合Stopwatch.Frequency换算,但多数业务不需要 - 跨线程共享
Stopwatch实例不安全,每个测量任务应独占一个实例或用Stopwatch.StartNew()创建新实例
异步操作测时别漏掉 await 的挂起/恢复开销
用 Stopwatch 包裹 await 表达式时,它只统计同步执行段,不包含线程挂起、调度等待、I/O 完成回调等异步延迟。比如 await File.ReadAllTextAsync(...),Stopwatch 测出来可能是 0.2ms,但实际用户感知延迟是 200ms —— 因为磁盘读取本身不在 CPU 时间里。
使用场景:区分 CPU 密集型耗时 vs I/O 密集型耗时;定位是代码慢还是外部依赖慢。
- 测纯异步 I/O:用
Stopwatch包住整个await行,它反映的是“从发起到完成”的端到端耗时(含调度) - 想排除调度干扰:改用
Environment.TickCount64(毫秒级,低开销),但它仍无法分离 I/O 真实耗时 - 生产环境排查慢请求:优先用分布式追踪(如 OpenTelemetry),而不是手工插
Stopwatch
毫秒级时间处理最麻烦的从来不是 API 调用,而是搞不清你要的到底是“墙上时间”“CPU 时间”还是“I/O 延迟”。选错类型,后面所有优化都跑偏。










