skip to content

Monotonic and Wall Readings

time.Now attaches a monotonic reading that Sub and Since use for elapsed time, but marshalling, Round and UTC strip it, silently switching you back to a wall clock that can jump.

part ofGo (Golang)overview, primer and where to startread it →
on this pageshow

questions

4

What two clock readings does Go's time.Now() put into one time.Time, and which one does time.Since use?

level: juniorimportance: must knowfreq 50%

answer

  1. one value, two clocks
  2. telling time versus measuring time
  3. the suffix you see when you print it
  4. subtraction ignores the calendar reading
  5. no separate timer API to call

basics

~20 s

time.Now() returns a time.Time holding both a wall-clock reading, which tells the calendar date and time and can be stepped by the system, and a monotonic reading, which only moves forward. time.Since subtracts using the monotonic reading.

solid answer

~40 s

A `time.Time` from `time.Now()` carries two readings inside one value. The wall-clock reading is what you format and print; the operating system can step it forward or backward when it synchronises the clock. The monotonic reading is an offset from an arbitrary point near process start that never jumps and never goes backwards. Go picks between them automatically: time-*telling* operations such as formatting use the wall reading, while time-*measuring* operations — `Sub`, `Before`, `After`, `Equal`, and therefore `time.Since` and `time.Until` — use the monotonic readings when both operands have one. That is why `time.Since(start)` stays correct even if the system clock is corrected mid-measurement, with no special API to call. Printing the value with `%v` shows the monotonic part as an `m=+…` suffix.

code

go · 6 lines
go
start := time.Now()
// e.g. 2026-09-02 10:04:05.123456789 +0000 UTC m=+0.000123456
fmt.Println(start)

elapsed := time.Since(start) // subtracts monotonic readings
fmt.Println(elapsed)

go deeper

for a junior

Be ready to say that time.Now gives you both a wall-clock reading and a monotonic reading in one value, and that time.Since(start) is the normal, correct way to measure how long something took.

for a middle

Explain the split precisely: telling time uses the wall reading, measuring time uses the monotonic reading, and measuring falls back to the wall clock as soon as either value has lost its monotonic part.

for a senior

Show that you check for the monotonic suffix when a duration looks wrong, and that you know a measurement only stays monotonic while the start value stays an untouched in-process time.Time.

for a principal

Frame the design point: Go put both readings in one type so ordinary code is correct by default, and the price is a value whose comparison semantics depend on invisible state your conventions have to protect.

## One value, two clocks An operating system exposes two different notions of time. The **wall clock** (also called the time-of-day clock) answers "what is the date and time right now?" It is the one that can be corrected: a time-synchronisation daemon can slew it or step it, an administrator can set it, a virtual machine can resume with a stale clock. The **monotonic clock** answers "how much time has passed?" It counts from an arbitrary, meaningless origin — usually something like boot or process start — and is guaranteed never to go backwards. Most languages make you choose between two different functions. Go does not. `time.Now()` returns a single `time.Time` value that contains **both** readings, and the standard library decides which one an operation should use. ## Telling time versus measuring time The rule the `time` package documents is: the wall clock is for telling time, the monotonic clock is for measuring time. - Operations that **tell** you the time — formatting the value, asking for its year or hour, interpreting it in a location — use the wall-clock reading. - Operations that **measure** — `t.Sub(u)`, `t.Before(u)`, `t.After(u)`, `t.Equal(u)`, `t.Compare(u)` — use the monotonic readings, **provided both values carry one**. If either operand has no monotonic reading, these operations fall back to the wall-clock readings. `time.Since(start)` is defined as the elapsed time since `start`, and `time.Until(deadline)` as the time remaining; both are built on `Sub`, so both inherit the monotonic behaviour. In the common case — you captured `start` with `time.Now()` in this process, and you call `time.Since(start)` in the same process — the subtraction is a pure monotonic subtraction and a clock correction in between cannot corrupt it. ## Seeing the monotonic reading The monotonic reading has no textual format of its own: there is no layout element for it, and it is deliberately omitted from every serialised form. But the `String` method includes it for debugging, so printing a value shows something like: ``` 2026-09-02 10:04:05.123456789 +0000 UTC m=+0.000123456 ``` Everything up to `UTC` is the wall-clock reading; the `m=+0.000123456` tail is the monotonic reading expressed as an offset in seconds from the process's monotonic origin. The presence or absence of that suffix is the fastest way to check whether a particular value will be compared monotonically. A value with no suffix has only a wall-clock reading. ## Why the origin is meaningless The monotonic reading is only comparable **inside one process on one machine**. Its zero point is arbitrary, it is not an epoch timestamp, and it cannot be compared with a monotonic reading taken by another process or another host. That is exactly why Go hides it: you never read it as a number, you only ever subtract two `time.Time` values that both have one. It is also why every encoding of a `time.Time` drops it — sending it anywhere would be sending a number that means nothing at the other end. ## The practical consequence For everyday code this means: ```go start := time.Now() doWork() elapsed := time.Since(start) ``` is already the correct way to measure a duration in Go. You do not need to reach for a separate high-resolution timer, and you do not need to defend against the clock being adjusted — as long as `start` is a value you obtained from `time.Now()` and kept as a `time.Time`, without passing it through any operation that discards the monotonic reading. Converting it, rounding it, or serialising and reloading it quietly turns the measurement back into a wall-clock subtraction, which is where the surprising results come from.

  • Can you compare the monotonic reading of a time.Time taken on one machine with one taken on another?
    No. The monotonic reading counts from an arbitrary origin private to that process on that machine, so the number means nothing anywhere else. That is why Go never exposes it as a value you can read, and why every encoding of a `time.Time` omits it. Cross-process ordering has to use the wall-clock reading, with all the caveats that implies.
  • If time.Since is monotonic, what still uses the wall-clock reading in an ordinary program?
    Everything that tells time rather than measures it: formatting a timestamp for a log line or an API response, asking for `Year`, `Hour` or `Weekday`, interpreting the value in a location, and any comparison where one of the two values has lost its monotonic reading. Deadlines you persist and re-read are wall-clock too.
  • Does time.Now() cost more because it reads two clocks?
    It does read both, and on some platforms that is two calls into the kernel's fast time path rather than one. It is still cheap enough for ordinary use, but it is a real cost in very hot loops that timestamp every operation — a reason to time a batch once rather than each item, not a reason to avoid `time.Now` generally.

It is like a stopwatch glued to the back of a wall calendar: the calendar page can be corrected if it was hung wrong, but the stopwatch keeps ticking forward regardless, and Go reads whichever face suits the question.

saying these in an interview costs you the question

  • Says time.Now returns only a wall-clock timestamp
  • Thinks Go has a separate monotonic clock function you must call
  • Believes time.Since re-reads the system date and subtracts calendar values
  • Treats the monotonic reading as an epoch timestamp you can compare across machines
  • Claims Go has no protection against the clock being adjusted mid-measurement
open as a page

Why can two time.Time values for the same instant differ under ==, and when must you use Equal?

level: middleimportance: should knowfreq 52%

basics

~20 s

== compares a time.Time's whole representation: its wall reading, its monotonic reading and its location pointer. Two values for the same instant differ if one still carries a monotonic reading or sits in another location. Equal compares only the instant.

open as a page

Which time.Time operations strip its monotonic clock reading, and what changes once one has?

level: middleimportance: should knowfreq 42%

basics

~20 s

Operations that reinterpret or recompute the wall clock strip it: UTC, Local, In, Round, Truncate, AddDate, and every encoding such as MarshalJSON or GobEncode. Add keeps it. Once stripped, Sub, Before, After and Equal fall back to the wall clock.

open as a page

time.Since returns a negative duration for a start time.Time your harness reloaded from JSON — how do you diagnose and fix it?

level: seniorimportance: should knowfreq 30%

basics

~20 s

Decoding drops the monotonic reading, so time.Since fell back to subtracting wall clocks, and the system clock was stepped backwards between the two readings. Print both values and look for the m= suffix, then measure from an in-process time.Time instead.

open as a page