How std::chrono Measures Time and Durations in C++
The C++ <chrono> library provides a robust,
type-safe framework for precise time measurement and calendar
operations. By decoupling time tracking into three distinct,
interconnected components—clocks, time points, and
durations—std::chrono allows developers to benchmark code
execution at nanosecond resolution or track real-world wall-clock dates
without risking unit-conversion errors. This article explains the
internal mechanics of these components, how to measure execution
intervals accurately, and how system time points represent
calendar-based events.
The Three Pillars of std::chrono
To eliminate ambiguity between spans of time and specific moments,
std::chrono separates time measurement into three
fundamental concepts:
- Durations (
std::chrono::duration): Measure an amount of elapsed time, defined as a count of ticks over a specific fractional second unit (e.g., 5 milliseconds, 10 seconds). - Time Points (
std::chrono::time_point): Represent a specific point in time relative to a clock's fixed reference point, known as the epoch. - Clocks: Bind a
time_pointto an actual physical mechanism or operating system interface, defining the epoch and tick rate.
Standard Clocks in C++
C++ provides several standard clocks, each tailored to different operational requirements:
std::chrono::steady_clock: A monotonic clock that is guaranteed never to be adjusted. Its ticks advance at a uniform rate and cannot jump backward, making it the required choice for measuring execution intervals and benchmarking.std::chrono::system_clock: The system-wide real-time clock representing wall-clock time. It can be adjusted by network time synchronization (NTP) or user changes, meaning it can jump forward or backward.std::chrono::high_resolution_clock: The clock with the shortest tick period available on the implementation. In practice, standard library implementations often define this as an alias for eithersystem_clockorsteady_clock.
Measuring Precise Durations
To measure code execution without interference from system time
changes, use std::chrono::steady_clock. Taking the
difference between two time_point objects produces a
std::chrono::duration:
#include <iostream>
#include <chrono>
#include <thread>
int main() {
auto start = std::chrono::steady_clock::now();
// Code under test
std::this_thread::sleep_for(std::chrono::milliseconds(50));
auto end = std::chrono::steady_clock::now();
// Difference results in a duration
auto elapsed = end - start;
// Convert duration to a specific precision
auto elapsed_us = std::chrono::duration_cast<std::chrono::microseconds>(elapsed).count();
auto elapsed_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(elapsed).count();
std::cout << "Elapsed: " << elapsed_us << " microseconds\n";
std::cout << "Elapsed: " << elapsed_ns << " nanoseconds\n";
}Compile-Time Unit Conversion and Safety
Durations use std::ratio at compile time to define
conversion factors. For example, std::chrono::milliseconds
uses std::ratio<1, 1000>.
C++ allows implicit conversions when converting from a coarser unit
to a finer unit (e.g., seconds to milliseconds), because no precision is
lost. However, converting from a finer unit to a coarser unit (e.g.,
nanoseconds to milliseconds) causes truncation and requires explicit
casting using std::chrono::duration_cast.
Handling System Time Points
While steady_clock benchmarks intervals,
std::chrono::system_clock tracks human-readable real-world
time. A time_point from a system_clock
represents the duration that has elapsed since the clock's epoch—almost
universally the Unix epoch (January 1, 1970, 00:00:00 UTC).
Converting System Points to Human Time
In C++20 and later, std::chrono provides direct
formatting and timezone capabilities. Prior to C++20, system time points
are typically converted to a std::time_t for
formatting:
#include <iostream>
#include <chrono>
#include <ctime>
int main() {
// Capture current wall-clock time
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
// Convert to time_t for legacy C-style date handling
std::time_t now_c = std::chrono::system_clock::to_time_t(now);
std::cout << "Current time: " << std::ctime(&now_c);
}C++20 expanded this functionality by introducing calendar features
(std::chrono::year_month_day) and timezone support
(std::chrono::zoned_time), allowing conversions between
physical durations, UTC, and localized calendar dates without manual
math.