std::chrono
Type-safe time and duration library — durations, clocks, time points, and C++20 calendar and timezone types.
std::chronosince C++11A type-safe time library that models durations (quantities of time), time points (moments anchored to a clock), and clocks (sources of time), extended in C++20 with civil-calendar types, IANA timezone support, and additional precision clocks.
Overview
std::chrono is built on three orthogonal concepts:
duration<Rep, Period>— a tick count of typeRepwhere each tick spansPeriod(astd::ratio) seconds. Arithmetic between durations of different periods compiles only when the conversion is lossless, or when you explicitly callduration_cast.time_point<Clock, Duration>— a point in time measured from a clock's epoch, statically bound to that clock. Subtracting twotime_points of the same clock yields aduration; subtracting across clocks is a compile error.- Clocks — tag types exposing
now()and adurationtypedef. C++11 introduced three; C++20 added four more with precisely specified epochs.
C++20 substantially extended the library: civil-calendar types (year_month_day, weekday, month_day, …), IANA timezone support (time_zone, zoned_time), parsing via std::chrono::parse, and new clocks.
Syntax
Duration template and predefined aliases
// Primary template
template<class Rep, class Period = std::ratio<1>>
class std::chrono::duration;
// C++11 predefined aliases
std::chrono::nanoseconds // duration<long long, std::nano>
std::chrono::microseconds // duration<long long, std::micro>
std::chrono::milliseconds // duration<long long, std::milli>
std::chrono::seconds // duration<long long>
std::chrono::minutes // duration<long long, std::ratio<60>>
std::chrono::hours // duration<long long, std::ratio<3600>>
// C++20 predefined aliases
std::chrono::days // duration<int, std::ratio<86400>>
std::chrono::weeks // duration<int, std::ratio<604800>>
std::chrono::months // duration<int, std::ratio<2629746>> // 30.436875 days
std::chrono::years // duration<int, std::ratio<31556952>> // 365.2425 daysUser-defined literals require using namespace std::chrono_literals; (C++11):
auto t = 2h + 30min + 15s; // chrono::hours, minutes, seconds
auto d = 500ms + 250us; // chrono::milliseconds, microseconds
auto n = 100ns; // chrono::nanosecondsC++20 literal trap:
15dcreatesstd::chrono::day{15}— a calendar day number, not adaysduration.2025ycreatesstd::chrono::year{2025}— a calendar year, not ayearsduration. There are no UDL suffixes fordays,weeks,months, oryearsdurations; construct them directly:std::chrono::days{7}.
Duration operations
using namespace std::chrono;
// Implicit conversion is allowed only when lossless
seconds s = 5min; // OK: minutes → seconds is exact
// minutes m = 90s; // Error: potential loss of precision
// Explicit conversion truncates toward zero
auto m = duration_cast<minutes>(90s); // 1min, not 1.5
// C++17: directed rounding
floor<seconds>(1500ms).count(); // 1
round<seconds>(1500ms).count(); // 2 (nearest)
ceil<seconds>(1500ms).count(); // 2
abs(-500ms).count(); // 500 // C++17
// Raw tick value
milliseconds{2500}.count(); // 2500
duration<double, std::ratio<1>>{0.5}.count(); // 0.5 — fractional secondsClock comparison
| Clock | Monotonic | Epoch | C++ version |
|---|---|---|---|
steady_clock | Yes | Unspecified | C++11 |
system_clock | No | Unix epoch (C++20: guaranteed) | C++11 |
high_resolution_clock | Impl-defined | Impl-defined | C++11 |
utc_clock | Yes | Unix epoch + leap seconds | C++20 |
tai_clock | Yes | 1958-01-01 (TAI) | C++20 |
gps_clock | Yes | 1980-01-06 (GPS) | C++20 |
file_clock | Impl-defined | Implementation-defined | C++20 |
high_resolution_clock is an alias for steady_clock on most implementations (GCC/libstdc++, recent MSVC). Never assume its epoch or monotonicity; prefer steady_clock explicitly.
Examples
Timing a block of code
#include <chrono>
#include <print> // C++23
template<class F>
std::chrono::microseconds timed(F&& f) {
auto start = std::chrono::steady_clock::now();
f();
return std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::steady_clock::now() - start);
}
auto us = timed([] { process_batch(); });
std::println("elapsed: {} µs", us.count());
// C++20: duration types are directly formattable
std::println("elapsed: {}", us); // "12345µs"Sleeping with steady deadlines
#include <chrono>
#include <thread>
using namespace std::chrono_literals;
std::this_thread::sleep_for(100ms);
// Absolute deadline prevents drift accumulation in loops
auto next = std::chrono::steady_clock::now();
for (int i = 0; i < 10; ++i) {
next += 200ms;
do_periodic_work();
std::this_thread::sleep_until(next);
}Wall-clock timestamps and formatting (C++20)
#include <chrono>
#include <format> // C++20
auto now = std::chrono::system_clock::now();
// Truncate to seconds resolution
auto now_s = std::chrono::floor<std::chrono::seconds>(now);
// ISO 8601 UTC
auto utc = std::format("{:%Y-%m-%dT%H:%M:%SZ}", now_s);
// "2026-05-22T14:23:05Z"
// Local civil time via IANA timezone database (C++20)
auto zt = std::chrono::zoned_time{
std::chrono::current_zone(),
std::chrono::system_clock::now()
};
auto local = std::format("{:%Y-%m-%d %H:%M:%S %Z}", zt);
// "2026-05-22 16:23:05 CEST"Calendar date arithmetic (C++20)
#include <chrono>
using namespace std::chrono;
// Construct a specific date
year_month_day launch{year{2026}, June, day{1}};
// Convert a system_clock time_point to a calendar date
year_month_day today = floor<days>(system_clock::now());
// Duration between two dates — convert to sys_days first
days diff = sys_days{launch} - sys_days{today};
// Day of week
weekday wd{sys_days{today}};
bool is_friday = (wd == Friday);
// Last day of a month (C++20)
year_month_day_last last_feb = year{2026} / February / last;
year_month_day end_of_feb{last_feb}; // 2026-02-28Parsing a date string (C++20)
#include <chrono>
#include <sstream>
std::chrono::sys_days date;
std::istringstream ss("2026-06-01");
ss >> std::chrono::parse("%Y-%m-%d", date);
// date == sys_days for 2026-06-01Converting between clocks (C++20)
#include <chrono>
auto sys_now = std::chrono::system_clock::now();
// system_clock ignores leap seconds; utc_clock counts them
auto utc_now = std::chrono::clock_cast<std::chrono::utc_clock>(sys_now);
// time_point_cast: change duration granularity, keep the clock
auto at_second = std::chrono::time_point_cast<std::chrono::seconds>(sys_now);Best Practices
Always use steady_clock for measuring elapsed time. Only use system_clock when you need a wall-clock value to display or store as a timestamp. The two are not substitutable: system_clock can jump backward on NTP corrections or forward on DST transitions.
Pass duration objects across API boundaries, never raw integers. A parameter typed std::chrono::milliseconds self-documents its unit and rejects unit mismatches at the call site. An int64_t timeout_ms parameter is a latent bug.
Know which rounding mode you need. duration_cast truncates; floor, round, and ceil (C++17) give directed rounding. For display, round is usually correct. For deadline computation, ceil avoids delivering early.
Seed RNGs with steady_clock, not time(0). time(0) has one-second resolution; two processes spawned in the same second get identical sequences.
auto seed = std::chrono::steady_clock::now().time_since_epoch().count();
std::mt19937_64 rng{static_cast<uint64_t>(seed)};For calendar date arithmetic, use C++20 types. Do not implement leap-year logic or month-end arithmetic manually. year_month_day handles these correctly; converting to sys_days for duration arithmetic is the idiomatic approach.
Common Pitfalls
system_clock for elapsed time
// Bad: can go backward or jump
auto start = std::chrono::system_clock::now();
do_work();
auto elapsed = std::chrono::system_clock::now() - start; // may be negative
// Good
auto start = std::chrono::steady_clock::now();
do_work();
auto elapsed = std::chrono::steady_clock::now() - start; // always >= 0C++20 calendar literals are not durations
using namespace std::chrono_literals;
auto x = 7d; // std::chrono::day{7} — a calendar day number, NOT a duration
auto y = 2025y; // std::chrono::year{2025} — a calendar year, NOT a duration
// To construct durations for days/weeks/months/years:
auto seven_days = std::chrono::days{7};
auto two_years = std::chrono::years{2};duration_cast truncates, not rounds
// duration_cast truncates toward zero
std::chrono::duration_cast<std::chrono::seconds>(999ms).count(); // 0, not 1
// When nearest-value is needed:
std::chrono::round<std::chrono::seconds>(999ms).count(); // 1
std::chrono::round<std::chrono::seconds>(500ms).count(); // 0 (round to even)Month overflow in calendar arithmetic
using namespace std::chrono;
year_month_day d{year{2026}, January, day{31}};
// Adding one month naively produces an invalid date
auto next = d.year() / (d.month() + months{1}) / d.day();
next.ok(); // false — 2026-02-31 does not exist
// Converting to sys_days normalises the overflow (Feb 31 → Mar 3)
year_month_day normalised{sys_days{next}};
// For end-of-month clamping, use year_month_day_last explicitly
auto eom = d.year() / (d.month() + months{1}) / last; // 2026-02-28Comparing .count() values of different duration types
std::chrono::seconds s{1};
std::chrono::milliseconds ms{1000};
s.count() == ms.count(); // false: 1 == 1000
s == ms; // true: implicit conversion, then compareNever compare .count() values across duration types. Compare the duration objects directly; the library applies the appropriate unit conversion.
C++20 calendar and timezone
C++20 transforms <chrono> from a measurement library into a full civil-calendar system. The additions cover date types (year, month, day, weekday, year_month_day, …), IANA timezone support via std::chrono::time_zone and std::chrono::zoned_time, and std::chrono::parse for reading date/time strings.
// C++20
#include <chrono>
using namespace std::chrono;
// Construct dates using operator/ overloads
year_month_day d1 = 2026y / June / 22d; // year/month/day
year_month_day d2 = 22d / June / 2026y; // day/month/year
year_month_day d3 = June / 22d / 2026y; // month/day/year
// Validate
d1.ok(); // true
year_month_day bad{year{2026}, February, day{30}};
bad.ok(); // false
// Arithmetic over calendar months and years
year_month_day next_year = d1 + years{1}; // 2027-06-22
year_month_day two_months_later = d1 + months{2}; // 2026-08-22Caveat:
monthsandyearsarithmetic can produce invalid dates (e.g., Jan 31 + 1 month = Feb 31). Always call.ok()or normalise throughsys_daysafterward.
std::chrono::year_month_day
year_month_day is the primary C++20 civil-calendar date type, storing a year, month, and day component separately. Convert to sys_days for duration arithmetic, and back to year_month_day to recover calendar fields.
// C++20
#include <chrono>
using namespace std::chrono;
// Today as a calendar date
year_month_day today{floor<days>(system_clock::now())};
// Extract components
int y = static_cast<int>(today.year());
unsigned m = static_cast<unsigned>(today.month());
unsigned d = static_cast<unsigned>(today.day());
// Duration between two dates (always work in sys_days)
year_month_day birth{year{1990}, March, day{15}};
days age_days = floor<days>(system_clock::now()) - sys_days{birth};
int age_years = static_cast<int>((age_days / days{365}).count()); // approx
// Last day of the current month
auto end_of_month = today.year() / today.month() / last;
year_month_day eom{end_of_month}; // e.g. 2026-06-30year_month_day is not directly comparable to time_point; the intermediate sys_days conversion (which is a time_point<system_clock, days>) is the bridge between the two worlds.
Time zone conversions
std::chrono::zoned_time pairs a time_point with an IANA timezone rule, handling DST and UTC offsets automatically. The timezone database is accessed via std::chrono::locate_zone and std::chrono::get_tzdb.
// C++20
#include <chrono>
#include <format>
auto utc_now = std::chrono::system_clock::now();
// Attach a timezone
std::chrono::zoned_time zt_ny{"America/New_York", utc_now};
std::chrono::zoned_time zt_tok{"Asia/Tokyo", utc_now};
std::chrono::zoned_time zt_local{std::chrono::current_zone(), utc_now};
// Get the local time_point and UTC offset
auto local_tp = zt_ny.get_local_time();
auto utc_tp = zt_ny.get_sys_time();
auto info = zt_ny.get_info(); // sys_info: offset, abbrev, DST flag
auto offset_h = std::chrono::duration_cast<std::chrono::hours>(info.offset);
// Format in local time
std::string s = std::format("{:%Y-%m-%d %H:%M:%S %Z}", zt_ny);
// "2026-06-22 10:23:05 EDT"// C++20 — convert between two named zones
auto to_zone = [](std::chrono::system_clock::time_point tp,
std::string_view from_zone,
std::string_view to_zone_name) {
std::chrono::zoned_time from{from_zone, tp};
return std::chrono::zoned_time{to_zone_name, from.get_sys_time()};
};Platform note: The IANA database is embedded in libc++ (Apple, most Linux) and available as an optional install (
/usr/share/zoneinfo) on MSVC. Verify availability at runtime withstd::chrono::get_tzdb_list().
Formatting durations and time points (C++20)
C++20 makes all <chrono> types directly formattable via std::format, with strftime-style conversion specifiers extended for sub-second precision. No manual .count() conversion is required.
// C++20
#include <chrono>
#include <format>
#include <print> // C++23
using namespace std::chrono;
using namespace std::chrono_literals;
// Duration formatting — unit suffix appended automatically
milliseconds d1{1234};
std::println("{}", d1); // "1234ms"
std::println("{:%Q %q}", d1); // "1234 ms" (%Q = value, %q = unit)
// time_point formatting
auto now = system_clock::now();
std::println("{:%Y-%m-%d}", now); // "2026-06-22"
std::println("{:%T}", now); // "14:23:05.123456789" (full sub-second)
// Truncate before formatting to control precision
auto now_s = floor<seconds>(now);
std::println("{:%Y-%m-%dT%H:%M:%SZ}", now_s); // "2026-06-22T14:23:05Z"
// zoned_time: local time + timezone abbreviation
zoned_time zt{"Europe/Paris", now};
std::println("{:%F %T %Z}", zt); // "2026-06-22 16:23:05.123456789 CEST"
// calendar types
year_month_day ymd = 2026y / June / 22d;
std::println("{:%B %d, %Y}", ymd); // "June 22, 2026"
weekday wd{sys_days{ymd}};
std::println("{:%A}", wd); // "Monday"std::chrono::parse is the inverse of std::format, reading date/time strings using the same format codes. Use it to deserialise timestamps from files, databases, or network protocols.
// C++20
#include <chrono>
#include <sstream>
system_clock::time_point tp;
std::istringstream ss{"2026-06-22T14:23:05Z"};
ss >> std::chrono::parse("%Y-%m-%dT%H:%M:%SZ", tp);Benchmarking with high_resolution_clock
steady_clock is the right choice for benchmarking — it is guaranteed monotonic, whereas high_resolution_clock is an implementation-defined alias that may map to system_clock on some platforms. Wrap the pattern in a reusable template to avoid repetition.
// C++11 and later
#include <chrono>
#include <utility> // std::forward
#include <string_view>
#include <print> // C++23; replace with printf for C++11
template<class F, class... Args>
auto benchmark(std::string_view label, int iterations, F&& f, Args&&... args) {
using clock = std::chrono::steady_clock;
using ns = std::chrono::nanoseconds;
// Warm-up pass (fills instruction cache, branch predictor)
std::forward<F>(f)(std::forward<Args>(args)...);
auto start = clock::now();
for (int i = 0; i < iterations; ++i)
std::forward<F>(f)(std::forward<Args>(args)...);
auto stop = clock::now();
auto total = std::chrono::duration_cast<ns>(stop - start);
auto per_op = total / iterations;
std::println("{}: {} total, {} per op", label, total, per_op);
return per_op;
}// C++11 — preventing the compiler from optimising away the work
// Use a volatile sink or compiler barrier to avoid dead-code elimination
template<class T>
inline void do_not_optimise(T const& val) {
// GCC/Clang: asm volatile ("" : : "r,m"(val) : "memory");
// MSVC: _ReadWriteBarrier();
(void)val;
}Caveat: On modern CPUs, frequency scaling, turbo boost, and NUMA effects can produce high variance. Run at least 1 000 iterations, discard the first few results, and report median and P99 rather than mean.
Custom duration units
Any compile-time std::ratio can define a custom duration type, making domain-specific time units first-class citizens that participate in the usual duration_cast / arithmetic machinery.
// C++11
#include <chrono>
#include <ratio>
// Video frame durations
using frames_24fps = std::chrono::duration<long long, std::ratio<1, 24>>;
using frames_60fps = std::chrono::duration<long long, std::ratio<1, 60>>;
// Audio samples at 44100 Hz
using audio_samples = std::chrono::duration<long long, std::ratio<1, 44100>>;
// Fortnights (2 weeks)
using fortnights = std::chrono::duration<int, std::ratio<1209600>>;
void example() {
using namespace std::chrono;
frames_24fps f{48}; // 48 frames at 24 fps = 2 seconds
auto secs = duration_cast<seconds>(f); // 2s
auto ms = duration_cast<milliseconds>(f); // 2000ms
// Implicit conversion works when lossless
frames_60fps f60 = duration_cast<frames_60fps>(f); // 120 frames at 60fps
audio_samples samples{88200}; // 2 seconds of audio at 44.1kHz
auto s2 = duration_cast<seconds>(samples); // 2s
fortnights fn{3};
auto days = duration_cast<std::chrono::days>(fn); // 42 days
}Custom duration types are zero-overhead; all conversions are computed entirely at compile time via std::ratio arithmetic and the conversion factor is folded into a single multiply/divide.
std::chrono::tai_clock and GPS time (C++20)
C++20 adds tai_clock and gps_clock for domains that require time scales with no leap-second discontinuities. tai_clock counts from 1958-01-01 00:00:00 TAI; gps_clock counts from 1980-01-06 00:00:00 GPS. Both are monotonic and fully interconvertible with utc_clock.
// C++20
#include <chrono>
// Current time on each scale
auto sys = std::chrono::system_clock::now();
auto utc = std::chrono::clock_cast<std::chrono::utc_clock>(sys);
auto tai = std::chrono::clock_cast<std::chrono::tai_clock>(sys);
auto gps = std::chrono::clock_cast<std::chrono::gps_clock>(sys);
// As of 2026 there are 37 TAI-UTC leap seconds and 18 GPS-UTC leap seconds
// TAI is always ahead of UTC by 37 seconds (since 2017-01-01)
// GPS is always ahead of UTC by 18 seconds (since 2017-01-01)
auto tai_offset = std::chrono::duration_cast<std::chrono::seconds>(
tai.time_since_epoch() - utc.time_since_epoch()); // +37s
// Convert back to system_clock for display
auto back_to_sys = std::chrono::clock_cast<std::chrono::system_clock>(tai);// C++20 — practical: timestamp a log entry in TAI (no leap-second ambiguity)
struct LogEntry {
std::chrono::tai_clock::time_point timestamp;
std::string message;
};
LogEntry make_entry(std::string msg) {
return {std::chrono::tai_clock::now(), std::move(msg)};
}Why TAI/GPS? Embedded systems, financial markets, and scientific instruments often need a smooth, continuous time scale without the discontinuities introduced by leap seconds in UTC.
utc_clockrepresents those discontinuities explicitly (it counts leap seconds), whereassystem_clock/POSIX time silently smears them over the surrounding window.
See Also
<ctime>—time_t,tm,strftimefor C-style time and legacy API interopstd::format/std::print(C++20/23) —{:%Y-%m-%d %H:%M:%S}format specifiers work directly onduration,time_point,zoned_time, and calendar typesstd::this_thread::sleep_for/sleep_until(<thread>) — chrono-aware thread blockingstd::filesystem::file_time_type—file_clock::time_pointreturned bystd::filesystem::last_write_timestd::condition_variable::wait_for/wait_until— chrono-aware condition variable blocking