skip to content

Why do measureTime / measureTimedValue use a monotonic clock instead of System.currentTimeMillis(), and what could go wrong if you timed code with wall-clock time?

level: middleimportance: should knowfreq 45%

answer

  1. Monotonic = only forward, no absolute date
  2. Wall-clock can jump (NTP, DST, manual) -> negative durations
  3. Backed by System.nanoTime() on JVM
  4. TimeSource.Monotonic + TimeMark.elapsedNow()
  5. Wall-clock is for timestamps, monotonic for intervals

basics

~20 s

A monotonic clock only moves forward at a steady pace, so it gives correct elapsed times. Wall-clock time can jump backward or forward (clock adjustments), which can make a measured duration wrong or even negative.

solid answer

~40 s

measureTime and measureTimedValue measure against TimeSource.Monotonic, a clock that is guaranteed never to go backward and is unaffected by changes to the system date/time. System.currentTimeMillis() returns wall-clock time, which can be adjusted by NTP synchronization, manual changes, or daylight-saving transitions; subtracting two wall-clock readings around a block can therefore produce a negative or inflated duration. The monotonic source has no meaningful absolute value (you cannot turn it into a date) — it is only useful for measuring intervals, which is exactly what timing needs. Internally on the JVM the monotonic source is backed by System.nanoTime(). So these helpers give correct, drift-free elapsed measurements precisely because they avoid wall-clock time.

code

kotlin · 7 lines
kotlin
import kotlin.time.TimeSource

val mark = TimeSource.Monotonic.markNow()
doWork()
val elapsed = mark.elapsedNow() // always a sane, non-negative Duration

// This is essentially what measureTime { doWork() } wraps for you.

go deeper

for a junior

Knows elapsed time should use a steady clock, not the wall clock.

for a middle

Explains monotonic vs wall-clock, names TimeSource.Monotonic and the negative-duration failure mode.

for a senior

Knows it is backed by System.nanoTime(), distinguishes timestamps from intervals, and picks the right tool per use case.

for a principal

Sets a team convention: monotonic for timeouts/benchmarks, wall-clock only for stored timestamps; reviews code that subtracts currentTimeMillis for timing.

## Two kinds of clocks - **Wall-clock time** (a.k.a. real time / civil time): what `System.currentTimeMillis()` and `Clock.System.now()` return — the current date and time. It can be changed: NTP daemons nudge it, users set it, daylight-saving shifts it. - **Monotonic time**: a counter that only ever increases at a steady rate. It has **no absolute meaning** — you cannot convert it to a calendar date — but the *difference* between two readings is a reliable elapsed interval. ## What the timing helpers use `measureTime` and `measureTimedValue` are built on **`TimeSource.Monotonic`** (the default monotonic source). Conceptually they capture a `TimeMark` before the block, run the block, then compute `mark.elapsedNow()`: ```kotlin // simplified mental model val mark = TimeSource.Monotonic.markNow() block() val elapsed = mark.elapsedNow() ``` On the JVM this monotonic source is backed by **`System.nanoTime()`**, which is exactly the API the docs tell you to use for elapsed-time measurements. ## What goes wrong with wall-clock time ```kotlin val start = System.currentTimeMillis() doWork() val elapsed = System.currentTimeMillis() - start // DANGER ``` If, during `doWork()`, the system clock is corrected backward (NTP, manual edit, DST fall-back), `elapsed` can become **negative** or wildly wrong. This is a classic source of flaky benchmarks, broken timeouts, and "negative duration" bugs. ## Consequences - Use monotonic time (and therefore `measureTime`/`measureTimedValue`, or `TimeSource.Monotonic.markNow()`) for **durations, timeouts, rate limits, benchmarks**. - Use wall-clock time (`Clock.System.now()`, `Instant`) only for **timestamps you store or display**. ## Key APIs named - `TimeSource.Monotonic` / `TimeSource.Monotonic.markNow()` returning a `TimeMark` - `TimeMark.elapsedNow()` returning a `Duration` - contrast: `System.currentTimeMillis()`, `Clock.System.now()` (wall-clock)

  • Which JVM API backs Kotlin's monotonic time source?
    System.nanoTime().
  • Can you turn a monotonic reading into a calendar date?
    No. Monotonic readings have no absolute reference point; they are only meaningful as differences (intervals).

Monotonic time is a stopwatch; wall-clock time is the clock on the wall. You time a race with a stopwatch, not by watching the wall clock that someone might reset mid-race.

saying these in an interview costs you the question

  • Claiming System.currentTimeMillis() is fine for timing
  • Saying monotonic time can be converted to a date/Instant
  • Not recognizing wall-clock adjustments can produce negative durations
  • Confusing currentTimeMillis with nanoTime

context