time.Now() の m= って何?

2026.7.3 Asakusa.go
あかがわまさとも

自己紹介

あかがわまさとも (X: @mathatomo57)

  • Go歴: もうすぐ2年
  • 最近: また仙台に行った

time.Now() してみたことありますか?

fmt.Println(time.Now())
2026-07-03 19:33:42.024824 +0900 JST m=+0.000209251
                                      ^^^^^^^^^^^^^^^^
                                      これ、何?

m= の正体 — モノトニッククロック

ウォールクロック モノトニッククロック
意味 カレンダー上の日時 単調増加するカウンタ
Go での表示 日時 (2006-01-02 15:04:05) m=+0.000...(プロセス起動起点)
特徴 巻き戻ることがある(NTP補正等) 絶対に巻き戻らない
用途 時刻の取得 経過時間の計測

Go の time.Now() は 両方を同時に取得 して1つの Time に詰めている

Go 公式ドキュメントより

Operating systems provide both a "wall clock," which is subject to changes for clock synchronization, and a "monotonic clock," which is not. The general rule is that the wall clock is for telling time and the monotonic clock is for measuring time. Rather than split the API, in this package the Time returned by time.Now contains both a wall clock reading and a monotonic clock reading; later time-telling operations use the wall clock reading, but later time-measuring operations, specifically comparisons and subtractions, use the monotonic clock reading.

— pkg.go.dev/time#hdr-Monotonic_Clocks

日本語訳

OSは「ウォールクロック」と「モノトニッククロック」の2つを提供している。ウォールクロックは時刻同期の影響を受けるが、モノトニッククロックは受けない。一般的に、ウォールクロックは時刻の取得に、モノトニッククロックは時間の計測に使われる。本パッケージでは APIを分割せず、time.Now が返す Time にウォールクロックとモノトニッククロックの両方の値を持たせている。以降の時刻取得操作にはウォールクロックが、比較や差分といった時間計測操作にはモノトニッククロックが使われる。

time.Time の中身

type Time struct {
    wall uint64     // ウォールクロック(ビットパッキング)
    ext  int64      // hasMono=1: モノトニック値 / hasMono=0: ウォール秒
    loc  *Location
}

wall の64bit:

┌──────────┬──────────────────────┬───────────────────┐
│ 1bit     │ 33bit                │ 30bit             │
│ hasMono  │ 秒 (1885年起点)       │ ナノ秒             │
└──────────┴──────────────────────┴───────────────────┘

モノトニッククロックを観察する

for i := 1; i <= 5; i++ {
    fmt.Println(time.Now())
    time.Sleep(50 * time.Millisecond)
}
2026-07-03 19:33:42.024824 +0900 JST m=+0.000209251
2026-07-03 19:33:42.075873 +0900 JST m=+0.051258792
2026-07-03 19:33:42.12702  +0900 JST m=+0.102405459
2026-07-03 19:33:42.178001 +0900 JST m=+0.153386918
2026-07-03 19:33:42.228934 +0900 JST m=+0.204319376

m= が 単調増加 している

モノトニックが剥がれる操作、残る操作

now := time.Now()
// 2026-07-03 19:33:54.18547 +0900 JST m=+0.000536293

剥がれる: Round, UTC, In, AddDate

.Round(0)        → 2026-07-03 19:33:54.18547 +0900 JST       ← m= 消えた!
.AddDate(0,0,0)  → 2026-07-03 19:33:54.18547 +0900 JST       ← m= 消えた!

残る: Add, Sub

.Add(1s)         → 2026-07-03 19:33:55.18547 +0900 JST m=+1.000536293  ✓
now2.Sub(now)    → 両方 m= ありならモノトニック差分を使う              ✓

Sub() は m= の有無で結果が変わる

start := time.Now()
time.Sleep(100 * time.Millisecond)
end := time.Now()
end.Sub(start)                             → 101.045542ms
end.Round(0).Sub(start.Round(0))           → 101.046ms
                                              ← 微妙に違う!

.Round(0) で m= を剥がすと、同じ時刻なのに結果が変わることがある

Sub() が使う時計の選び方

start end Sub() が使う時計
m= あり m= あり モノトニック差分
m= なし m= なし ウォール差分
m= あり m= なし ウォール差分
m= なし m= あり ウォール差分

両方に m= があるときだけ、モノトニック差分が使われる

time.Since もこの仕組みの上にある

func Since(t Time) Duration {
    if t.wall&hasMonotonic != 0 && !runtimeIsBubbled() {
        // Common case optimization:
        // if t has monotonic time, then Sub will use only it.
        return subMono(runtimeNano()-startNano, t.ext)
    }
    return Now().Sub(t)
}
  • t に m= があれば → モノトニック差分を直接計算(最適化)
  • なければ → Now().Sub(t) にフォールバック
  • Until も同じ構造

まとめ

time.Now() は2つの時計を持っている
「時刻を知る」→ ウォール、「時間を測る」→ モノトニック

モノトニックは ある操作で剥がれ、ある操作では残る
Sub() の結果はモノトニックの有無で変わる