skip to content

How do you measure how long a block of code takes using TimeSource.Monotonic, and why use it instead of System.currentTimeMillis()?

level: juniorimportance: must knowfreq 55%

answer

  1. markNow() -> TimeMark, elapsedNow() -> Duration
  2. Monotonic = steady, never goes backward
  3. Wall clock (currentTimeMillis) can jump
  4. ValueTimeMark is an inline value class (no alloc)
  5. Get a typed Duration, not a raw Long

basics

~10 s

Call TimeSource.Monotonic.markNow() before the code to get a TimeMark, then call mark.elapsedNow() after to get a Duration. It uses a steady clock that never jumps backward, unlike wall-clock millis.

solid answer

~30 s

TimeSource.Monotonic.markNow() returns a TimeMark (a ValueTimeMark) recording the current reading of the monotonic clock. After running your code, call mark.elapsedNow() to get a kotlin.time.Duration of how much time passed. The monotonic source is designed for measuring elapsed time: it is steady and not affected by NTP adjustments, daylight-saving, or the user changing the system clock, so it never goes backward. System.currentTimeMillis() is wall-clock time and can jump if the clock is adjusted, producing negative or wildly wrong durations. You get a typed Duration (with .inWholeMilliseconds, .inWholeNanoseconds, etc.) instead of raw longs you must subtract and unit-track yourself.

code

kotlin · 10 lines
kotlin
import kotlin.time.TimeSource

fun main() {
    val source = TimeSource.Monotonic
    val mark = source.markNow()
    Thread.sleep(50)
    val elapsed = mark.elapsedNow()
    println(elapsed)                    // e.g. 50.1ms
    println(elapsed.inWholeMilliseconds) // 50
}

go deeper

for a junior

Knows the markNow() -> elapsedNow() pair and that it returns a Duration.

for a middle

Explains why monotonic beats wall-clock for elapsed measurement and that it never goes backward.

for a senior

Mentions ValueTimeMark being an inline value class (zero-alloc) and that Monotonic maps to nanoTime on the JVM.

for a principal

Frames the wall-clock-vs-monotonic split as a domain decision and can reason about clock-source guarantees across platforms.

## The problem Measuring how long something takes seems trivial: read the clock before, read it after, subtract. But `System.currentTimeMillis()` reads the *wall clock* — the human calendar time. That clock can be moved: NTP can step it, daylight-saving changes it, a user can set it. If it moves backward mid-measurement you get a negative or absurd elapsed value. ## TimeSource and the monotonic clock A **`TimeSource`** is Kotlin's abstraction for "something I can read the current time-point from." The built-in one for measuring durations is **`TimeSource.Monotonic`** (a `TimeSource.WithComparableMarks`). It wraps a *monotonic* clock (`System.nanoTime()` on the JVM) — a clock that only moves forward at a steady rate and has no defined relationship to calendar time. It is the right tool for *elapsed* measurement, the wrong tool for "what time is it." ## TimeMark **`markNow()`** captures the current reading and returns a **`TimeMark`**. On `Monotonic` this is the inline value class `ValueTimeMark`, so capturing a mark allocates nothing. A `TimeMark` is a *point*, not a duration. To get the time since the mark, call **`elapsedNow()`**, which returns a **`kotlin.time.Duration`**: ```kotlin import kotlin.time.TimeSource val mark = TimeSource.Monotonic.markNow() doWork() val elapsed = mark.elapsedNow() // Duration println("took $elapsed") // e.g. "took 12.3ms" println(elapsed.inWholeMilliseconds) ``` ## Why Duration, not Long `Duration` is a typed, unit-safe value. You read it with `.inWholeMilliseconds`, `.inWholeNanoseconds`, `.inWholeSeconds`, or destructure with `toComponents`. No more "is this millis or nanos?" guessing that plagues raw-`Long` math. ## Rule of thumb - Measuring elapsed time / timeouts / benchmarks -> `TimeSource.Monotonic`. - Showing or storing a calendar time -> wall clock (`Clock`/`Instant`), not `Monotonic`.

  • Can you turn a TimeMark into a calendar timestamp like '2026-06-23 10:00'?
    No. A monotonic TimeMark has no relationship to wall-clock/calendar time; it is only meaningful for measuring durations between marks. Use Clock/Instant for calendar time.
  • What backs TimeSource.Monotonic on the JVM?
    System.nanoTime(), which is a monotonic high-resolution timer with no defined epoch.

A stopwatch (monotonic) vs the clock on the wall (wall-clock): the stopwatch only counts forward from when you pressed start, the wall clock can be reset to any time.

saying these in an interview costs you the question

  • Using System.currentTimeMillis() for benchmarking elapsed time
  • Thinking markNow() returns a Duration rather than a TimeMark
  • Believing a Monotonic mark can be formatted as a date
  • Subtracting raw nanoTime longs by hand instead of using Duration

context