How do you measure how long a block of code takes using TimeSource.Monotonic, and why use it instead of System.currentTimeMillis()?
answer
- markNow() -> TimeMark, elapsedNow() -> Duration
- Monotonic = steady, never goes backward
- Wall clock (currentTimeMillis) can jump
- ValueTimeMark is an inline value class (no alloc)
- Get a typed Duration, not a raw Long
basics
~10 sCall 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 sTimeSource.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 linesimport 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
Knows the markNow() -> elapsedNow() pair and that it returns a Duration.
Explains why monotonic beats wall-clock for elapsed measurement and that it never goes backward.
Mentions ValueTimeMark being an inline value class (zero-alloc) and that Monotonic maps to nanoTime on the JVM.
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