Guide

Operating systems expose two kinds of clock, and they answer different questions. Wall-clock time answers "what time is it?" It is seconds since the epoch, and it can jump forward or backward when the clock is adjusted. Monotonic time answers "how much time has passed here?" It never goes backward, but it has no meaning across machines or reboots.

Concept

A step is a sudden jump in the wall clock, from NTP correction or a manual change. A monotonic clock counts from an arbitrary starting point, often boot, and only increases.

The big idea

Measuring a duration is different from reading the time. Think of timing a race with a wall clock. If someone resets the wall clock mid-race, the times are wrong. A stopwatch never gets reset that way, so it's the right tool.

The rule is simple. Use monotonic time for durations: timeouts, latencies and lease expiry on the local machine. Use wall-clock time only to show people when something happened.

Walking through it

Subtracting two wall-clock readings measures a duration only if the clock doesn't step in between. If it does, the result can be negative or huge.

JavaScript
// Wrong: breaks if NTP steps the clock during the call.
const start = Date.now()
await callDependency()
const elapsedMs = Date.now() - start // can be negative or huge

// Right: monotonic, immune to clock steps.
const t0 = performance.now()
await callDependency()
const elapsed = performance.now() - t0

A runnable check of the monotonic version:

JavaScript
const t0 = performance.now()
await new Promise((resolve) => setTimeout(resolve, 50))
const elapsed = performance.now() - t0
console.log(elapsed >= 50 ? "waited at least 50 ms" : "too short")

Output:

waited at least 50 ms

Python has the same split. time.time() is the wall clock, and time.monotonic() is the stopwatch. A five-second deadline built on time.monotonic() can't be shortened by a clock step.

Monotonic values are not dates. A number such as 84,213 ms means nothing to a human, and it means nothing to a different machine. Convert to wall-clock time only when you show someone a time.

Common mistakes

  • Timing timeouts with Date.now(). A clock step can make the timeout fire early, or never.
  • Comparing monotonic values across machines. Each machine's counter starts from its own point, so the numbers don't line up.
  • Showing monotonic values to users. They aren't calendar times. Convert to wall-clock time for display.

Check yourself

  1. Which clock should measure how long a database call took? The monotonic clock, because it can't jump during the call.
  2. Can you compare a monotonic reading from one machine with one from another? No. Each counter starts at its own point, often boot, so the values have no shared meaning.

Key takeaways

  • Wall-clock time is for people. Monotonic time is for durations.
  • Subtracting wall-clock readings can produce negative or huge durations when the clock steps.
  • Use monotonic deadlines for timeouts and leases on one machine.
  • Don't compare monotonic values across machines.

Further learning

Kleppmann's lecture on physical time, linked on the clock drift and NTP page, explains why these clocks behave differently.

Next, read about last-write-wins and skew, which shows what happens when wall-clock timestamps decide a conflict.