Clocks and durations

std::chrono separates two ideas: a duration is an amount of time with a unit, and a time point is a moment on a clock. Durations add, subtract and compare, and the unit is part of the type, so a second and a millisecond are different types that the compiler will not mix by accident.

The clocks provide the time points. steady_clock only moves forward and is the one for measuring, system_clock is the wall clock and can jump when the machine adjusts its time. This lesson times a loop with the first and shows the second's current moment.

The clock and the duration are separate types on purpose: a duration can be measured, stored and compared without a clock, and a clock can be replaced by a test double. That is what lesson 086 does with time in a test.

Durations and their units

A duration has a count and a unit: seconds{2} is two seconds, milliseconds{500} half a second. Adding two durations gives a duration in the finer unit, which is why the sum below is 2500 milliseconds.

The program adds seconds and milliseconds and prints the raw count, then casts the sum to seconds. The cast truncates towards zero, so 2500 milliseconds become 2 seconds.

The type carries the unit, so the compiler keeps it straight. That is the difference from a bare int that means milliseconds in one function and seconds in another.

#include <chrono> #include <iostream> int main() { using namespace std::chrono; seconds whole{2}; milliseconds part{500}; auto total = whole + part; std::cout << total.count() << ' ' << duration_cast<seconds>(total).count() << '\n'; }

g++ -std=c++23 -Wall -Wextra duration.cpp -o duration
./duration

2500 2

duration_cast

duration_cast converts between units and truncates. Converting to a finer unit is exact, converting to a coarser one loses the remainder, and the cast is written out so the loss is visible in the source.

The program converts 1234 milliseconds to seconds and gets 1, then converts 2 seconds to milliseconds and gets 2000. The first conversion dropped 234 milliseconds.

Since C++17 the constructor accepts a duration of another unit when the conversion is exact, so milliseconds m = seconds{2}; works and the reverse needs the cast. The next section shows the compiler refusing the lossy direction.

#include <chrono> #include <iostream> int main() { using namespace std::chrono; milliseconds ms{1234}; std::cout << duration_cast<seconds>(ms).count() << ' ' << duration_cast<milliseconds>(seconds{2}).count() << '\n'; }

g++ -std=c++23 -Wall -Wextra cast.cpp -o cast
./cast

1 2000

Timing a loop with steady_clock

steady_clock::now() returns a time point that only moves forward, at a steady rate. Two calls around a piece of work give a duration that measures the work, not the time of day.

The program sums ten million integers and prints the sum and the elapsed milliseconds. The number of milliseconds is what this machine took on this run; a different machine, or a different load, gives another number.

The loop is short enough that the measurement includes the clock calls themselves. A real benchmark repeats the work and subtracts an empty loop, which lesson 118 does.

#include <chrono> #include <iostream> int main() { using namespace std::chrono; auto start = steady_clock::now(); long long sum = 0; for (int i = 0; i < 10000000; ++i) { sum += i; } auto end = steady_clock::now(); std::cout << sum << ' ' << duration_cast<milliseconds>(end - start).count() << " ms\n"; }

g++ -std=c++23 -Wall -Wextra clock.cpp -o clock
./clock

49999995000000 45 ms

system_clock time points

system_clock is the wall clock. now() returns a time point whose epoch is the system's, and subtracting two of them gives a duration, exactly as with steady_clock.

The program takes the current moment, adds a minute and prints the difference in seconds. The value is 60 whatever the machine's time is, which is why the output is stable while the moment is not.

A time point converts to the C time_t with system_clock::to_time_t when a library wants a calendar time. Lesson 073 formats the calendar date directly.

#include <chrono> #include <iostream> int main() { using namespace std::chrono; auto now = system_clock::now(); auto later = now + seconds{60}; std::cout << duration_cast<seconds>(later - now).count() << '\n'; }

g++ -std=c++23 -Wall -Wextra point.cpp -o point
./point

60

A lossy conversion needs a cast

Assigning milliseconds to seconds would drop the fraction, and the standard library refuses to do it implicitly. The compiler reports that the conversion from a duration with a ratio of 1/1000 to one with a ratio of 1 is not available.

The program assigns 1500 milliseconds to a seconds variable. The error names the two duration types and points at the assignment, and the fix is duration_cast<seconds>, which says that the loss is intended.

The rule protects the common case: a function that takes seconds cannot silently receive milliseconds and run a thousand times too long.

#include <chrono> int main() { using namespace std::chrono; milliseconds ms{1500}; seconds s = ms; return static_cast<int>(s.count()); }

g++ -std=c++23 -Wall -Wextra lossy.cpp -o lossy

lossy.cpp: In function 'int main()':
lossy.cpp:8:17: error: conversion from 'duration<[...],ratio<[...],1000>>' to non-scalar type 'duration<[...],ratio<[...],1>>' requested
8 | seconds s = ms;
| ^~

Exercises

  1. Time a loop that pushes a million ints into a reserved vector and into an unreserved one, and print both durations.
  2. Convert 90 minutes to hours and print the truncated and the exact value.
  3. Add 1500 milliseconds to a steady_clock time point and print the difference in microseconds.

Questions

A new set of questions in a new order every time. Choose one answer for each, then check.

  1. What does a duration store?

    Answer

    A count and a unit

    The unit is part of the type, so seconds and milliseconds do not mix silently.

  2. What does duration_cast do?

    Answer

    It converts between units, truncating

    The cast changes the unit and drops the fraction below it.

  3. Which clock should measure an interval?

    Answer

    std::chrono::steady_clock

    Only steady_clock is monotonic and unaffected by time adjustments.

  4. What is a time point?

    Answer

    A moment on a clock

    The time point is a clock's epoch plus a duration.

  5. What does this program print? #include <chrono> #include <iostream> using namespace std::chrono; int main() { milliseconds ms{2500}; seconds s = duration_cast<seconds>(ms); std::cout << ms.count() << ' ' << s.count(); }

    Answer

    2500 2

    The duration holds 2500 milliseconds, and the cast truncates to two seconds.

  6. What does this program print? #include <chrono> #include <iostream> using namespace std::chrono; int main() { seconds s{1}; milliseconds ms = s; std::cout << duration_cast<seconds>(1500ms).count() << ' ' << ms.count(); }

    Answer

    1 1000

    1500 milliseconds truncate to one second, and one second is a thousand milliseconds.

  7. What does this program print? #include <chrono> #include <iostream> int main() { auto start = std::chrono::steady_clock::now(); long total = 0; for (int i = 0; i < 1000; ++i) total += i; auto end = std::chrono::steady_clock::now(); std::cout << total << ' ' << (end - start >= std::chrono::nanoseconds::zero()); }

    Answer

    499500 1

    The loop sums 0 to 999, and the elapsed time is never negative on a steady clock.

  8. What does this program print? #include <chrono> #include <iostream> using namespace std::chrono; int main() { auto first = sys_days{year{2026} / 1 / 1}; auto second = sys_days{year{2026} / 3 / 2}; std::cout << (second - first).count(); }

    Answer

    60

    January has 31 days and February 28, so the difference is 31 plus 29, which is 60.

  9. What does the compiler report for this line? #include <chrono> using namespace std::chrono; int main() { milliseconds ms{1500}; seconds s = ms; return s.count(); }

    Answer

    conversion from a milliseconds duration to seconds is not implicit

    A lossy unit conversion needs duration_cast, and the implicit one is disabled.

  10. Why does seconds s = 1500ms; not compile?

    Answer

    The conversion would lose information

    Chrono refuses implicit conversions from a finer unit to a coarser one.

The code on this page was built with g++ 15.2.0 and run on Alpine Linux 3.24.3 (x86_64) in a Docker container. The programs whose section shows a compiler error were compiled until that error and were not run.

Article author: Arthur Isaev