Przejdź do głównej zawartości

<chrono> introduction

This article will summarize presentation by Howard Hinnant which he gave during CppCon 2016. Howard gives comprehensive walk through c++11 chrono library.

TL;DR (Summary)

C++11 introduced <chrono> library for handling time. Use it instead of <time.h> / <ctime> because it gives you better type safety and precision.
Library provides those concepts:
  • time points - moments in time (as in "noon, December 1st 2000", "2016-01-31 14:15:32")
  • time durations - "length" (amount) of time that passes between two time points ("two hours", "2563 milliseconds")
  • clocks - providers of time points (like wall clock, stopper)
Query clock to get time points; calculate difference between two time points to get time duration. You can use time points/duration with <thread> library for sleep, wait_for/wait_until functions.
Use auto, because otherwise type names are quite verbose.

Time Duration

std::time_duration is a measure of how much time passed. It is measured in seconds or its multiples (here multiples meaning greater as well as smaller amounts).


Seconds are easiest unit to reason about. They are also basic time unit internally in library, but this is not always apparent.
std::chrono::seconds is a class that stores how much time passed in seconds.
This is trivial type (trivially copy/move/construct/destructible).


By default internally it is signed long long / int64_t - simple arithmetic type of sizeof() == 8. When doing release build (with optimizations), there is no time penalty for using std::chrono instead of long long.
Despite internal representation, seconds are not implicitly convertible to/from integers.  This is because integers don't store information about unit of time and library uses type safety to protect us against conversion errors.


seconds s; // gibberish
seconds s{}; // zero-initialization
seconds s{3}; // 3 seconds
//seconds s = 3; ERROR: not implicitly constructible from int


// cout << s <<endl; // BAD
cout << s.count() << "s" <<endl; // GOOD, explicit cast


void foo(seconds d) {...}
// f(3); ERROR: not implicitly constructible from int
f (seconds{3}); // using r-value
f (3s); // only C++14


Arithmetic operations are supported, you can add and subtract seconds ( + - ). Again, there is no implicit conversion from int. Comparison is also supported with operators: >  <  <=  >=  ==  !=


The amount of time passed that can be represented chrono::seconds is limited by range of internal type. The range of allowed values can be checked by using static methods: seconds::min(), seconds::max().
Because by default internal representation is signed, we can get negative time duration. This may seem strange, but think about this example: Race car reached finish line in record time, beating previous champion by 5 seconds. How much time time longer was it riding than record? Minus 5 seconds.

std::chrono::milliseconds and others

Milliseconds type is the same as seconds, only has 1000-times smaller range and 1000-times greater precision. Other duration types follow similar pattern. There are types from nanoseconds to hours, each covers a range of at least ±292 years. You can easily create other duration types.

Implicit conversions

All duration types convert implicitly among themselves as long as conversion is not lossy. So this works:
void foo(milliseconds d) {
    cout << d.count() << "ms" << endl;
seconds s{3};
// prints "3000ms"
but this doesn't:
void bar(seconds d) {
    cout << d.count() << "s" << endl;
milliseconds s{3001};
// bar(s); ERROR

Comparison and arithmetics

Comparing seconds and milliseconds works using operators <, <=, >, >=, ==, !=. When comparing with const of other type, conversion is free (compile time).
Arithmetic with operators + and - is allowed. "Mixed mode" arithmetic (again, conversions) works and produced result of greater precission:
2s + 3ms == 2003ms;



Implicit casting (using some duration type when other duration type is expected) works when it is safe. Lossless (converting e.g. seconds to milli) always works. Lossy is blocked at compile time to protect against loss of information.
If there is truncation, it will not compile. If using floating point representation (I will explain in a second), conversion always works but is prone to rounding error.
Always prefer implicit conversion. If it works, it is exact and correct.


When we are OK with losing precision, we have to explicitly tell this to the compiler. To force the loss use duration_cast<target_type>. duration_cast always truncates toward 0:
seconds p_down = duration_cast<seconds>(3400ms); // 3s
seconds p_up = duration_cast<seconds>(3600ms); // 3s
seconds n_down = duration_cast<seconds>(-3400ms); // -3s
seconds n_up = duration_cast<seconds>(-3600ms); // -3s

For C++1z there are proposed additional truncating functions (to test them I had to install GCC-7, 6.3 was not enough):
  • floor<> toward negative infinity 
  • ceil<> toward positive infinity 
  • round<> toward nearest and towards even on a tie
    // floor goes down (to negative infinity)
    seconds p_floor = floor<seconds>(3600ms); // 3s
    seconds n_floor = floor<seconds>(-3600ms); // -4s

    // ceil goes up
    seconds p_ceil = ceil<seconds>(3600ms); // 4s
    seconds n_ceil = ceil<seconds>(-3600ms); // -3s

    // round goes to closest integer
    seconds p_round4 = round<seconds>(3400ms); // 3s
    seconds p_round6 = round<seconds>(3600ms); // 4s
    seconds n_round4 = round<seconds>(-3400ms); // -3s
    seconds n_round6 = round<seconds>(-3600ms); // -4s

    // but on draw, it goes to even number (not up!)
    seconds p_round5u = round<seconds>(3500ms); // 4s
    seconds p_round5d = round<seconds>(2500ms); // 2s
    seconds n_round5u = round<seconds>(-3500ms); // -4s
    seconds n_round5d = round<seconds>(-2500ms); // -2s
Rounding to even, not up, was surprizing to me. It turns out however, that rounding always up has side effect of shifting average up. By rounding to even, we usually avoid shifting it (see SO answer).


There is one more way to convert duration types: .count() method. It returns number of ticks in duration in internal representations. This is method of last chance, because it totally forgoes type safety.
However it may be needed when you work with I/O or legacy code. Reverse conversion (long long into duration) is achieved by duration constructor.

Prefer implicit

  1. Always prefer to use implicit conversion.
  2. If it is not possible, use duration_cast<> and others
  3. As last chance, use .count(), but avoid it at all cost


Storage type (Rep)

I wrote earlier that duration types are in fact signed long long by default. This is customizable by
using seconds32 = std::chrono::duration<int32_t>;
This will work with whole library but operate on 32 bit int instead of 64bit long long (will be cheaper).
We might as well use only unsigned type, or larger type. Only constrain is this must be arithmetic type. Even double/float will work.
using secondsReal = std::chrono::duration<double>;
When using real type, all casts are allowed, even those to less precise durations. After such cast new value will just use fraction part: 3123ms == 3.123s.


Period tells us how long (in real time) is a tick. A tick is unit of time that is counted by Rep. Period is represented by ratio<N,D> (a fraction) relative to 1 second. For example second has ratio<1,1>, millisecond ratio<1,1000> and hour ratio<3600,1>.
Thanks to this feature, you can create custom duration classes that work with any time unit you want. Your class Fortnight with ratio<14*24*3600,1> will easily convert/cast to std::seconds without any additional work.
It is nice to know, that Period is not stored in instances of Duration. Instead it is part of class known only during compilation. Thanks to this, all conversions are calculated at compile time and there is no memory footprint.
If you don't specify the period in duration<> template instantiation, <1,1> will be assumed. So std::duration is measuring seconds by default.

Time points

Time points represents... points in time. When duration means "how much time passed", time point means "when exacly". Think of them as a single read-out of a calendar / wall clock.


We can add/substract duration to time_point and receive time_point.
We can only substract two time_points and receive duration.
Lastly, we can compare 2 time_points using arithmetic operators <, <=, >, >=, ==, !=.
    auto tp1 = system_clock::now();
    auto tp2 = system_clock::now();
    auto diff = tp2 - tp1;
    assert (diff > 1999ms);
    assert (diff < 1h);
    assert (diff != 2s); // sleep is not perfect
    assert (duration_cast<seconds>(diff) == 2s);

Internal representation

Internally time_point class contains at run time a single duration member. At compile time it also contains Clock that it is based on.
Similarily to duration, you can choose storage type. Then we can implicitly cast to more precise representation or force cast with time_point_cast to coarser representation. Even more dangerous is explicit cast duration to/from time_point with .time_since_epoch(). As last resort use .time_since_epoch().count() (same caveats as for .count() with duration), that returns number of ticks in internal duration.
time_point<system_clock> tp = system_clock::now();  // auto is good, use it.
static_assert(sizeof(tp) == sizeof(long)); 
time_point<system_clock, seconds>tp_sec = time_point_cast<seconds>(tp);
time_point<system_clock, milliseconds> tp_msec = time_point_cast<milliseconds>(tp);
// see why auto is good?

// cout << tp << endl; // does not compile
auto time_passed = tp.time_since_epoch();
// time_passed is of type duration<system_clock::duration>,
// for me duration<nanoseconds>
long ticks = time_passed.count(); 
In example above I avoided using auto to show types explicitly. In production code this would be bad, uncomfortable style.


Clock object are source of time_points. Time_points created by one clock interact with other time_points created by this same clock. time_points created by different Clocks are not interoperable, and trying to combine them will cause compile-time error.
There are two Clock classes that you need to know:
  • system_clock is like a calendar / wall clock. Time_points represent time of day and have meaing on their own (realtime property). You need to expect that time may be adjusted when daylight saving changes or user changes time using system settings or ntp-update. You can however expect that comparing time_poinst acquired in separate processes makes some sense (system-wide property).
  • steady_clock is like a stopwatch. Tells us how much time passed since some time in the past (usually since start of the system), but absolute value of time_points is worthless. Those time_point are always good for comparing during the same program run - their difference has real meaning.
There is third clock, high_resolution_clock. In theory it was supposed to be clock with most precise ticks. However it does not give any guarantee. You can't count on it being steady and monotonic like steady_clock and neither is it system-wide nor represents real time. Best thing is to avoid it.
    auto tp_sys = system_clock::now();
    auto tp_inc = steady_clock::now();
    auto tp_sys2 = system_clock::now();
    auto tp_inc2 = steady_clock::now();

    << "system clock before " <<tp_sys.time_since_epoch().count()  <<endl;
    << "system clock after  " <<tp_sys2.time_since_epoch().count() <<endl;
    << "steady clock before " <<tp_inc.time_since_epoch().count()  <<endl;
    << "steady clock after  " <<tp_inc2.time_since_epoch().count() <<endl;
Outputs (seconds part in bold)
system clock before 1494532574013087456
system clock after  1494532579013272202
steady clock before 56987669915614
steady clock after  56992670099734

Casting to string

Sometimes you will want to have textual representation of time. There are several methods for this.

Howard Hinnant date library

Howard is author of CppCon presentation and main author of <chrono> library. introduces logical extensions for <chrono>, among them streaming chrono types to iostream.
#include "date.h"
#include <iostream>

    using namespace date;
    using namespace std::chrono;
    std::cout << system_clock::now() << " UTC\n";
which just outputs for me: 2017-05-11 20:10:07.930671545 UTC. It is possible to alter format of printed string. Also possible is customizing timezone, using header tz.h; otherwise time is printed in UTC, regardless of local zone.
Parts of this library are currently proposed for inclusion into standard.

Conversion to time_t (C way)

time_t is old structure to contain time_points. It has seconds precision, so is not always sufficient.
#include <iostream>
#include <chrono>
#include <ctime> // C Time Library

using namespace std::chrono;

int main()
    system_clock::time_point p = system_clock::now();

    std::time_t tt = system_clock::to_time_t(p);
    char *string_rep = std::ctime(&tt);
    std::cout << string_rep << std::endl; // for example : Thu May 11 22:06:24 2017

    // now with custom formating
    //std::tm *tm = gmtime(&tt); // for UTC time representation
    setlocale(LC_TIME, "pl_PL.utf8");
    std::tm *tm = localtime(&tt); // for local time representation

    char buf[70];
    strftime(buf, sizeof buf, "%A %c", tm);
    std::cout << buf << std::endl; // for example : czwartek czw, 11 maj 2017, 22:06:24
Other formating options are available here.

Other informations


std::chrono library does not handle slipseconds and timezones. Their efffect is visible through system_clock readings, but only if some external mechanism adjusts correctly clock of operating system.


In order to use suffix 's' for std::seconds in C++14, you need
using namespace std::literals::chrono_literals
Note however, that this may cause conflicts with suffix 's' for std::string. For this reason you need to decide which of two literals you want to import in given scope.
All suffixes available in C++14 are ns, us, ms, s, min, h.

See also

Falsehoods programmers believe about time - a reason why you should never write your own time library: you are most likely underqualified.


Popularne posty z tego bloga

Cleaning up #includes

Cleaning up includes In previous article I described why you should care about clean #includes and what rules I suggest to achieve it. If you already have a project that doesn't follow those rules, it would be tedious manual task to implement my advises. This article describes how some tools can help you answer these questions:
How to replace include guards with pragma once in whole project?How to sort includes in whole project?How to remove redundant includes and add missing ones?How to replace angle bracket includes with quotes only for project headers? #pragma once with include-guard-convert.pyInclude-guard-convert is simple tool that will find all include guards and change them to #pragma once. Tool doesn't need installation, simply download script from github and run it on your header files:
wget chmod +x ./ proj_path/**/*.…

GDB - beyond basics

GDB is console debugger that every Linux-using programmer heard about. It is not however easy to learn. Greg Law in his CppCon talk presented some of obscure, but useful features.

Text user interface Normally we use GDB with command line interface (CI). Beyond this, GDB has TUI based on Curses library. To activate it, use keyboard shortcut ctrl-x-a (hold ctrl, press x, unpress x, press a). Now you can see the code as you go through it.
ctrl-x-a - activate/deactivate TUIctrl-l - when screen gets messed up, use it to redraw. Happens when program prints to stdout/stderrctrl-p / ctrl-n - since you can't use arrows to reuse previously written command, use ctr-p/n instead of arrow up/down
ctrl-f / ctrl-n are arrows left / right
ctrl-a / ctrl-e are home / end (all those are copied from Emacs)ctrl-x-2 - second window (assembly). Shell You can run shell commands inside GDB command line. Just use keyword "shell" at the beginning. Examples:
shell psshell cat temporary_file.txtshell k…

Tidy #includes

Tidy #includes Having tidy includes in C++ project is often thought as low priority. It can however help you with making your code less fragile. This is doubly important when your project is a library that will be used by other people.
I propose that "good" includes should follow those rules:
Associated header (foo.hpp for foo.cpp) always comes firstAny file should have all the headers it needs. System headers are in <angle_brackets>, project headers are in "quotes"Project headers are first, STL headers are last, between them are libraries in order of decreasing locality.Use #pagma once instead of include guards Within groups decided by 4), alphabetic order is nice to have.  Associated header is first The header that declares class / methods should be the first to include. It also should have the same file name as current source file, except for extension. Some tools depend on this - for example IDEs use this to implement "go to declaration/implementatio…