Managing time with std::chrono

C++11 introduces the <chrono> library helpful to work with time spans. In this post, I go through its most important parts and show some examples.

Durations

Duration is the simplest type comprising of a ratio (e.g. 1:1000 for milliseconds) and the number of ticks. A single object is guaranteed to hold a timespan nearing 300 years.

You can implicitly cast values to more precise types, and there’s duration_cast<> to handle unsafe casting to more general ones. Duration supports basic arithmetics, so you can quickly add durations, multiply them, etc. Many of these operations will be evaluated at compilation time.

There are predefined durations, from an hour to a nanosecond. C++14 introduces duration literals, what makes it even easier to express timespans in code.

$ clang++ -std=c++14 durations.cpp -o durations.exe && ./durations.exe
24h
1440min
86400s
86400000000ms
86400000000000ns

40min

3h + 52min + 40s - 2ns = 13959999999998ns

Clocks

We need the clock to get durations from the system. Chrono clock is a type containing some basic information: what type it returns, whether it is a stable clock (so that we avoid problems with system clock adjustments), and ability to get the current time.

The standard library provides three of them:

  • system_clock represents time as held by the system. It also provides to_time_t() method.
  • stable_clock is guaranteed to be monotonic
  • high_resolution_clock returns as precise time as possible

A clock returns a time point, from which we extract further information.

Time points

std::chrono::timepoint is a duration since the start of the epoch of the clock (Unix epoch is the de facto standard, at least for system_clock). Every time point is associated with its clock and data from different ones is not interoperable.

Timepoints have basic algebra that returns durations. That makes it easy to calculate how long action took:

$ clang++ -std=c++14 timepoints.cpp -o timepoints.exe && ./timepoints.exe
Operation took: 277ms

Types Summary

Here’s a quick diagram showing how these things work together:

Basic overview of std::chrono

Basic overview of std::chrono

Example: simple game loop

We want to run input-update-render loop continuously, but it makes no sense to render scene trillion times a second since nobody would notice a change. Instead, loop allocates any free time to updates, so that game runs “no faster” than 60 frames per second.

$ clang++ -std=c++14 gameloop.cpp -o gameloop.exe && ./gameloop.exe
Loop:
> updating...
> updating...
> updating...
> updating...
> updating...
Loop:
> updating...
> updating...
> updating...
> updating...
> updating...
Loop:
> updating...
> updating...
> updating...
> updating...
> updating...
> updating...

Notice, that the third frame could squash one update more in 16ms.

Example: getting Unix timestamp

Notice that timestamps could be different since there are two separate time reads in the code:

$ clang++ -std=c++14 unixtimestamp.cpp -o unixtimestamp.exe && ./unixtimestamp.exe
std::chrono only: 1492084951
with std::ctime: 1492084951

No date nor calendar

Unfortunately, our library does not support dates out of the box. That is by design (I think I saw some official explanation for this, will update when I recall the place). Good news is, Howard Hinnant wrote an excellent date library (GitHub).

See also