tezvyn:

Go's Time: Wall Clocks vs. Monotonic Clocks

AI-drafted, machine-checkedSource: pkg.go.devbeginner

Go separates telling time (wall clock) from measuring it (monotonic clock) to prevent errors from system clock changes. Use `time.Sub` for reliable timing and `time.Format` for display. The footgun: formatting uses a magic date, not YYYY-MM-DD.

WHY IT EXISTS Computers need to do two different things with time: tell a user what time it is (wall clock) and measure how long something took (duration). System clocks can be adjusted forwards or backwards by users or network protocols, which would corrupt duration measurements if you only used the wall clock. Go's time package was designed to handle this distinction safely.

THE MENTAL MODEL Think of a time.Time object as having two clocks inside it. One is a regular wall clock for telling the date and time, like 2024-07-26 10:00:00. The other is a simple stopwatch (the monotonic clock) that started at an arbitrary point and only ever ticks forward. When you ask for the calendar date, you get the wall clock. When you measure the difference between two time.Time values, Go uses the stopwatch readings for accuracy.

HOW IT WORKS The time package provides two main types: time.Time for an instant and time.Duration for an interval. time.Duration is an integer representing a count of nanoseconds. When you call time.Now(), Go captures both the system's wall clock time and its monotonic clock reading into a time.Time struct. When you subtract two times using t2.Sub(t1), Go uses the difference between their monotonic clock readings. This ensures that if the system clock was set backwards between t1 and t2, you still get a positive, correct duration. Operations like t.Year() or t.Format() use the wall clock part, as they relate to calendrical concepts.

WHEN TO USE IT Use time.Time and time.Duration for any time-related logic. A common pattern is start := time.Now() followed by elapsed := time.Since(start) to reliably measure how long an operation took. Use time.Sleep(d) to pause a goroutine. Use time.Parse and time.Format for converting between time.Time objects and string representations, like handling timestamps from APIs or databases.

WHEN NOT TO USE IT The time package assumes the Gregorian calendar and does not handle leap seconds. For high-precision scientific or astronomical calculations that require accounting for leap seconds, you may need a specialized library. For simple benchmarks where performance is paramount, some might use lower-level calls, but time.Since is almost always sufficient and safer.

ONE CANONICAL EXAMPLE Measuring function execution time is a classic use case. First, start := time.Now(). Then, after the operation completes, elapsed := time.Since(start). This code is robust even if an NTP daemon adjusts the system clock while the operation is running. The elapsed duration will be accurate because time.Since (which is syntactic sugar for time.Now().Sub(start)) uses the monotonic clock readings from both time.Time values.

Read the original → pkg.go.dev

Get five bites like this every day.

Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.