time.Now() 返回的 Time 里藏着第二个时钟:单调时钟。它让 Sub 和 Since 不受系统时间调整影响,却也让 == 比较、map key 变得出人意料。这篇用实验验证哪些操作保留它、哪些去掉它,以及后果。
time.Now().String() 的尾部: m=+0.000114459 同一个值 Round(0) 之后的完整输出:2026-10-10 13:55:29.982922 +0800 CST Round(0) 之前的完整输出:2026-10-10 13:55:29.982922 +0800 CST m=+0.000114459
time.Now() 返回的 Time 同时有墙钟读数和单调读数。打印出来尾部的 m=+… 就是后者;文档说墙钟用来报时,单调时钟用来量时。Sub、Since 等在两边都有单调读数时只用它,所以系统时间被校正也不会算出负的耗时。
time.Now():有 m= t.Add(time.Hour):有 m= t.AddDate(0,0,1):无 m= t.Round(time.Second):无 m= t.Truncate(time.Second):无 m= t.Round(0):无 m= t.UTC():无 m= t.Local():无 m= t.In(time.UTC):无 m= time.Unix(t.Unix(), 0):无 m= time.Date(…):无 m=
Add 保留,AddDate、Round、Truncate、UTC、Local、In 去掉,Date 和 Unix 构造的值本来就没有。想去掉的标准写法是 t.Round(0)。
t == t.Round(0):false;t.Equal(t.Round(0)):true 经过 JSON 序列化再反序列化:t == u:false;t.Equal(u):true;t.Sub(u) = 0s(JSON 只保留到纳秒的墙钟时间,单调读数丢了) 用 time.Time 当 map 的 key:用 t 查 true;用 t.Round(0) 查 false(同一个时刻!) 同一时刻、不同时区:d == l:false;d.Equal(l):true
== 比较的不只是时刻,还有时区和单调读数:t == t.Round(0) 是 false,而 Equal 是 true;用 time.Time 当 map 的 key,用同一时刻去掉单调读数的值就查不到。JSON 往返之后也是 == 不等、Equal 相等。比较时刻永远用 Equal,不要用 ==;做 map key 前先 Round(0) 或者转成 Unix 时间。
time.Now() 32 ns/次;time.Since(t) 15 ns/次;t.Round(0) 2 ns/次
time.Now() 约 32 ns,time.Since 约 15 ns,Round(0) 约 2 ns。对绝大多数程序不是问题。
The general rule is that the wall clock is for telling time and the monotonic clock is for measuring time. The canonical way to strip a monotonic clock reading is to use t = t.Round(0). If Times t and u both contain monotonic clock readings, the operations t.After(u), t.Before(u), t.Equal(u), t.Compare(u), and t.Sub(u) are carried out using the monotonic clock readings alone, ignoring the wall clock readings. If either t or u contains no monotonic clock reading, these operations fall back to using the wall clock readings. On some systems the monotonic clock will stop if the computer goes to sleep. Note that the Go == operator compares not just the time instant but also the Location and the monotonic clock reading. See the documentation for the Time type for a discussion of equality testing for Time values. For debugging, the result of t.String does include the monotonic clock reading if present. If t != u because of different monotonic clock readings, that difference will be visible when printing t.String() and u.String().
要证明“系统时间被调整时 Sub 仍然正确”,需要真的修改系统时钟,这需要管理员权限,我没有做,所以这一条只引用文档。休眠时单调时钟停止的说法同样来自文档。