Using std::chrono

The goal of this post is to show how the <chrono> library can be used to solve a practical but not that obvious problem. There is a lot of good material in the Internet where one can learn <chrono> from, for instance a series of lectures by Howard Hinnant — the author of the library:

And you probably know <chrono> already. But knowing the library may still not be enough to appreciate its power, scope and design. So the goal here is to start with the problem, and see which parts of the library need to be employed to solve it.

The task is this. We want to display the current system time, but rounded (down) to 20 minutes, so that 14:55 is displayed as 14:40.

Let’s introduce a namespace alias to make the examples shorter:

namespace krn = std::chrono;

We start from measuring the current time using the system clock:

krn::time_point p = krn::system_clock::now();

There is a number of clocks that we could choose from:

  • system_clock — this is the system-wide real time wall clock; its measurements can be mapped on the C-style time_t. Its epoch used to be unspecified in C++11 but since C++ 20 the clock actually represents the Unix Time. However, it is not monotonic, as system time can be adjusted by the admin at any point. So this is not a good candidate for measuring time intervals.
  • steady_clock — this clock is intended for handling interval measurements. It guarantees that clock ticks have identical time duration, and that its values constantly increase. On the other hand, it does not have to correspond to the wall-clock: it could be implemented as a tick count from the last system reboot.
  • high_resolution_clock — this is the clock with the highest resolution provided by the implementation. However, it is not guaranteed to be either monotonic or associated with a wall-clock time (in fact, implementations make it an alias on either system_clock or steady_clock), so it cannot be used portably.

C++20 has widened the range of options:

  • utc_clock — represents Coordinated Universal Time (UTC), which includes leap seconds.
  • tai_clock — represents International Atomic Time (TAI), does not include leap seconds.
  • gps_clock — represents Global Positioning System (GPS) time, does not include leap seconds.
  • file_clock — this is used for file times in the filesystem.

A clock is a type that assigns interpretation to numeric values representing time. The type of the clock encodes information such as:

  • epoch — the starting point from which the passage of time is measured.
  • period — the time interval of a single clock tick.

For our exercise it is enough to use the system time, so we have:

krn::time_point p = krn::system_clock::now();

krn::time_point represents the time elapsed since the clock’s epoch. The question is how. It probably stores a single integer, we have to know how to interpret it. krn::time_point looks like a type, but it is in fact a class template with its arguments deduced. The type is really:

krn::time_point<krn::system_clock, krn::system_clock::duration>

So, the time_point is parametrized by the clock and the duration. From the clock we can figure out the epoch; and we could also figure out the duration, but instead it is a separate template parameter: this is in case we want to change the resolution of time points. And this is exactly what we need to solve our task.

A word about duration types in C++. They are all instances of class template std::chrono::duration:

template<
  class Rep,
  class Period = std::ratio<1>
> class duration;

Rep is a numeric type (integral or floating-point) that is used to store the number of ticks. Period is trickier: it is technically also a type, but in fact this type encodes a value. This value is a “tick period”: it says what fraction of the second one tick represents. This means that the following duration declaration represents time interval with 1 second resolution:

krn::duration<int, std::ratio<1>>;

And the following duration declarations represent time intervals with 1 millisecond resolution:

krn::duration<int, std::ratio<1, 1000>>;
krn::duration<int, std::milli>;

So, what duration is krn::system_clock::duration actually? It is unspecified. The implementation will choose something reasonable and appropriate for your platform; but you cannot rely on these choices in portable programs. But this will not be our concern anyway, because we will create our own — portable — resolution: a 20-minute resolution:

using TwentyMins = krn::duration<int, std::ratio<20 * 60>>;

Now when our system_clock returns type

krn::time_point<krn::system_clock, krn::system_clock::duration>

We will convert it to

krn::time_point<krn::system_clock, TwentyMins>

And we will be rounding down. For this, the <chrono> has function floor:

krn::time_point p1 = 
  krn::floor<TwentyMins>(krn::system_clock::now());

Now we have the count of 20-minute intervals from the Unix Time epoch till now. The only remaining task is to print it out. In C++20 this is easy with the <format>:

std::cout << std::format("{:%Y-%m-%d %H:%M}\n", p1); 

In C++11 the library is not present, so we would have to call a number of additional functions to achieve a similar effect. But what we could do instead is to use the open source {fmt} library by Victor Zverovich, which was used as a reference implementation for <format>:

#include <fmt/chrono.h>

// ...
std::cout << fmt::format("{:%Y-%m-%d %H:%M}\n", p1);

An that’s it. You can find a full working example here. I would like to thank my colleague Darek Chlipała for sharing this interesting use case with me.

This entry was posted in programming and tagged , , . Bookmark the permalink.

11 Responses to Using std::chrono

  1. In my use of chrono I am attempting for the format to be passed to a function as an argument. A version or two ago it worked. Now it no longer does. Every possible type of format argument fails to compile.

  2. Mariusz Jaskółka says:

    Nice, but what about time zones? `std::time_point` stores only a duration since epoch so I assume it will always print the UTC time. As of C++17 I had to use C-style `std::localtime` from to format a string. C++20 introduced some time_zone utility (https://en.cppreference.com/w/cpp/chrono/time_zone) but it doesn’t seem to be well-documented yet.

    • Ok, so the next post will show how to do the same thing but in local time.
      You are correct that with system_clock we are using Unix Time, which assumes we are in GMT timezone. It is not exactly UTC, because we are not including leap seconds.

    • chlorie says:

      IMHO the API documentation per se is fine, but the lack of concrete examples makes piecing information together really difficult, although once you figure it out it all totally makes sense.
      You could get your local timezone by `std::chrono::current_zone()` which gets you a pointer into the timezone database, and then use `to_local(sys_tp)` to convert system clock time points into local ones. That is `std::chrono::current_zone()->to_local(std::chrono::system_clock::now())`

  3. > krn::duration;

    This should be `krn::duration`.

  4. Pingback: Local Time | Andrzej's C++ blog

  5. flurbygrass says:

    Arithmetic with chrono literals is soooo underrated… https://godbolt.org/z/8dxGer37z

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.