Monotonic Clock vs. Wall Clock: Understanding Time Measurement on Linux
AI generated
$
/etc
Linux · Time Measurement · Kernel · System Programming
Monotonic Clock vs. Wall Clock
two clocks that should never be confused

The Linux kernel maintains not one but several clocks at the same time, and picking the wrong one for the wrong job leads to bugs that only surface during an NTP correction or a manual time jump. This guide explains the difference between CLOCK_REALTIME and CLOCK_MONOTONIC, why timeouts and retry logic should avoid the wall clock, and how scripts stay robust against time jumps.

14 min read CLOCK_MONOTONIC · CLOCK_REALTIME · clock_gettime Linux · Kernel · Bash · systemd

1. Two Clocks in the Kernel: CLOCK_REALTIME and CLOCK_MONOTONIC

The Linux kernel provides applications with not just one clock, but several distinct clocks, each with its own guarantee and its own purpose. CLOCK_REALTIME, commonly called the wall clock, delivers the actual calendar time, the kind a human would read off a clock, including date and time of day. CLOCK_MONOTONIC, the monotonic clock, delivers no calendar time at all, only a continuously increasing counter value since some arbitrary, unspecified starting point, usually system boot.

The crucial difference lies in the guarantee: the wall clock can change at any time, forward or backward, for example through an NTP correction, a manual adjustment by an administrator, or a timezone change. The monotonic clock, by definition, guarantees it will never run backward, regardless of whatever else happens to the system time. This guarantee makes the monotonic clock the only correct tool for anything that measures time differences or durations, while the wall clock remains responsible exclusively for displaying and storing calendar points in time.

In practice, many developers mix up both concepts, often without noticing, because date +%s and similar commands return the wall clock by default. Anyone using that to measure durations builds a script that works correctly under normal circumstances but fails precisely when an NTP correction changes the system time during the measurement. Understanding monotonic clock vs. wall clock is therefore not an academic detail, but a practical prerequisite for robust system programming.

2. Why the Wall Clock Can Move Backward

A freshly booted server often has an imprecise system time until the NTP client, such as chrony or systemd-timesyncd, completes its first synchronization with a time server. This first correction can shift the system time forward or backward by seconds or even minutes, depending on how much the internal hardware clock deviates from the actual point in time. A process that uses the wall clock for timing measurement exactly during this correction can, in rare cases, see a time difference that is negative even though real time has actually passed.

This also happens during ongoing operation: chrony and similar services normally smooth out small deviations gently, so called slewing, where the clock runs slightly faster or slower over a longer period instead of jumping abruptly. Larger deviations, for example after a prolonged network outage or a manual date -s command by an administrator, are instead often applied as an abrupt jump, so called stepping. It is exactly this jump that is the reason wall clock based timing is considered error prone in production systems.

3. Monotonic Clock in Practice: uptime and clock_gettime

On Linux, the monotonic counter can be observed at several levels. The file /proc/uptime shows in its first column the seconds since system boot, derived directly from the kernel's internal monotonic clock. For more precise measurements inside custom programs, the POSIX function clock_gettime(CLOCK_MONOTONIC, &ts) is available, returning a structure with seconds and nanoseconds since the unspecified reference point. The nanosecond component makes this function suitable even for high resolution performance measurements inside individual function calls.

At the command line level, date +%s.%N does provide nanosecond precision, but is still based on the wall clock and therefore unsuitable for pure duration measurement. For shell scripts that want to measure durations correctly, a direct look into /proc/uptime is a better fit instead, combined with a simple difference calculation between a start and an end value.


#!/usr/bin/env bash
# Comparing wall clock vs monotonic time for duration measurement
set -euo pipefail

# WRONG for duration measurement: wall clock can jump backward
start_wall="$(date +%s.%N)"
sleep 2
end_wall="$(date +%s.%N)"
echo "Wall clock delta: $(echo "$end_wall - $start_wall" | bc) seconds"

# RIGHT: monotonic counter from /proc/uptime, immune to NTP jumps
start_mono="$(awk '{print $1}' /proc/uptime)"
sleep 2
end_mono="$(awk '{print $1}' /proc/uptime)"
echo "Monotonic delta: $(echo "$end_mono - $start_mono" | bc) seconds"

# A small C program calling clock_gettime(CLOCK_MONOTONIC, ...) directly
cat > /tmp/mono_demo.c << 'EOF'
#include <stdio.h>
#include <time.h>
int main(void) {
    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC, &ts);
    printf("monotonic: %lld.%09ld\n", (long long)ts.tv_sec, ts.tv_nsec);
    return 0;
}
EOF
gcc -o /tmp/mono_demo /tmp/mono_demo.c && /tmp/mono_demo

4. Impact on Timeouts and Retry Logic

A common bug pattern in hand rolled retry mechanisms: a script remembers the start time with date +%s, repeatedly calculates elapsed time against a timeout value inside a loop, and aborts once that value is exceeded. If an NTP correction runs during this loop and sets the system clock back by several minutes, the calculated difference can suddenly turn negative or stay unexpectedly small, causing the timeout to never trigger and the loop to keep running far beyond the actually intended time, in the worst case indefinitely.

If the wall clock instead jumps forward, for example due to a rough miscorrection, a timeout can falsely trigger immediately, even though in reality hardly any time has passed. Both scenarios are rare in practice, but exactly for that reason hard to reproduce and debug once they do occur. Anyone implementing timeouts, retry intervals, or rate limits should therefore fundamentally use the monotonic clock, never the wall clock, since only the monotonic clock guarantees that a measured difference always corresponds to actually elapsed time.


#!/usr/bin/env bash
# Timeout loop that survives an NTP time jump during execution
set -euo pipefail

readonly TIMEOUT_SECONDS=30
start_mono="$(awk '{print $1}' /proc/uptime)"

while true; do
  now_mono="$(awk '{print $1}' /proc/uptime)"
  elapsed="$(echo "$now_mono - $start_mono" | bc)"

  if (( $(echo "$elapsed >= $TIMEOUT_SECONDS" | bc -l) )); then
    echo "[ERROR] Timeout after ${elapsed}s, giving up" >&2
    exit 1
  fi

  if curl -fsS -m 2 http://backend.local/health > /dev/null 2>&1; then
    echo "[OK] Backend is reachable after ${elapsed}s"
    break
  fi

  sleep 1
done

5. CLOCK_MONOTONIC_RAW and CLOCK_BOOTTIME: the Nuances

Besides the standard monotonic clock, Linux knows two related but distinct variants. CLOCK_MONOTONIC_RAW delivers the pure hardware counter time without any NTP adjustment at all, not even the gentle slewing that CLOCK_MONOTONIC normally experiences. This variant suits very precise, short duration measurements where even minimal adjustments by the NTP daemon are undesirable, for example in microsecond level benchmark measurements.

CLOCK_BOOTTIME differs in another, often overlooked detail: while CLOCK_MONOTONIC pauses during a device's suspend phase, such as a laptop in standby, CLOCK_BOOTTIME counts suspend time as well. For servers this difference is mostly irrelevant, since production servers usually never enter suspend mode, but for laptops and mobile devices running systemd timers it can be decisive, for example when a timer needs to catch up immediately after waking from standby.

6. Making Bash Scripts Robust Against Time Jumps

For the vast majority of administrative shell scripts, the approach shown above via /proc/uptime is entirely sufficient to measure durations robustly against time jumps. An additional, defensive pattern: for critical scripts whose runtime should be monitored, it is worth comparing the runtime measured via the monotonic clock against the runtime measured via the wall clock at the end of the script. A large discrepancy between the two values points to a time jump during execution and can be written into the log as its own diagnostic information.

For scripts implementing wait intervals with sleep, the monotonic clock is already implicitly in play: sleep itself is internally based on a monotonic kernel timer and is therefore already robust against wall clock jumps, unlike hand rolled wait loops that manually calculate elapsed time with date instead of simply using sleep directly.

7. Log Timestamps: Wall Clock Needed, but Careful With Deltas

For log files and database timestamps, the wall clock is indispensable, because a log entry needs to show when an event actually occurred in calendar terms, not how many seconds have passed since the last system boot. A monotonic counter would be meaningless for this purpose, since its reference point resets to zero on every reboot and has no relation whatsoever to an actual calendar date.

Caution is warranted, however, as soon as a time difference is calculated from two wall clock timestamps, for example to determine the duration between two log events. If an NTP correction occurred between the two timestamps, the calculated difference no longer reflects the actually elapsed time. For pure display and retention purposes the wall clock therefore remains correct, but for calculating time differences within a running process, only the monotonic clock is the correct choice.

8. systemd Timers and Their Internal Monotonic Logic

systemd timer units internally distinguish between calendar based expressions like OnCalendar, which are based on the wall clock, and monotonic expressions like OnBootSec or OnUnitActiveSec, which are based on the monotonic clock. A timer with OnUnitActiveSec=1h fires exactly one hour after the last start of the associated service unit, regardless of whether the wall clock was changed in the meantime by an NTP correction, because the internal calculation consistently uses the kernel's monotonic counter.

This difference explains why an OnCalendar timer can, under a larger manual time correction, trigger immediately or even multiple times, while a purely monotonic timer remains completely unaffected by such a correction. For time critical, recurring maintenance intervals where the exact wall clock time is irrelevant, but a reliable fixed spacing between two executions matters, a monotonic timer expression is therefore often the more robust choice compared to a calendar based expression.

9. Clock Types at a Glance

The following table summarizes the key Linux clocks and their respective guarantees, to make the right choice easier depending on the use case.

Clock Type Can Jump? Runs During Suspend? Typical Use
CLOCK_REALTIME Yes, forward and backward Yes Calendar time, log timestamps, databases
CLOCK_MONOTONIC No, only gentle slewing No, stops during suspend Timeouts, duration measurement, retry logic
CLOCK_MONOTONIC_RAW No, not even slewing No High precision microsecond level benchmarks
CLOCK_BOOTTIME No, only gentle slewing Yes, includes suspend time Laptops, mobile devices, timers after standby

As a rule of thumb: for anything that should be displayed, stored, or compared against a calendar date, CLOCK_REALTIME is correct. For anything that measures a duration or a time interval, CLOCK_MONOTONIC or one of its variants is the correct and robust choice, regardless of how often the system time is corrected in the background.

Mironsoft

System programming, backend robustness, and Linux infrastructure

Timeouts and retry logic that survive time jumps?

We review existing scripts and applications for wall clock based timing and replace critical spots with robust, monotonic time calculation that stays reliable even during NTP corrections.

Code Audit

Analysis for error prone wall clock usage in timeouts and retries

Refactoring

Converting critical time measurements to CLOCK_MONOTONIC based logic

systemd Timer Design

Choosing correctly between OnCalendar and monotonic timer expressions

10. Summary

Monotonic clock vs. wall clock is the central difference between two clocks Linux maintains simultaneously: CLOCK_REALTIME delivers the actual calendar time but can change at any moment through NTP corrections or manual intervention, even backward. CLOCK_MONOTONIC, by contrast, guarantees a counter value that never runs backward and is therefore the only correct tool for timeouts, retry intervals, and duration measurement.

Variants such as CLOCK_MONOTONIC_RAW and CLOCK_BOOTTIME cover special cases like high precision benchmarks or suspend aware timers. For log timestamps and databases, the wall clock remains indispensable; for calculating time differences within a running process, only the monotonic clock belongs in use. Consistently maintaining this distinction avoids bugs that only surface at the next major NTP correction.

Monotonic Clock vs. Wall Clock, the Essentials at a Glance

Wall Clock Can Jump

CLOCK_REALTIME can jump forward and backward due to NTP or manual intervention.

Monotonic Clock Always Advances

CLOCK_MONOTONIC guarantees by definition that it never runs backward.

Timeouts Need Monotonic

Duration measurement, retry logic, and timeouts should exclusively use the monotonic clock.

Logs Need Wall Clock

CLOCK_REALTIME remains necessary for calendar timestamps, unsuitable for deltas.

11. FAQ: Monotonic Clock vs. Wall Clock

1Difference between monotonic and wall clock?
Wall clock delivers calendar time and can jump, monotonic clock is guaranteed to never run backward.
2Why can the wall clock jump backward?
NTP corrections, manual adjustments, or an inaccurate hardware clock at boot.
3Which clock for timeouts?
Always use CLOCK_MONOTONIC.
4How do I measure monotonic time in Bash?
Via the first column of /proc/uptime.
5MONOTONIC vs. MONOTONIC_RAW?
RAW has no NTP adjustment at all, MONOTONIC has gentle slewing.
6What is CLOCK_BOOTTIME?
Like MONOTONIC but also counts suspend time. Relevant for laptops.
7Monotonic clock for log timestamps?
No, logs need the wall clock for actual calendar time.
8OnCalendar vs. OnUnitActiveSec?
OnCalendar uses wall clock, OnUnitActiveSec uses monotonic clock.
9Is sleep affected by time jumps?
No, sleep is already based internally on a monotonic timer.
10Why is date +%s unsuitable for durations?
It returns the wall clock, which can jump due to NTP.