Skip to content

Date and Time Utilities

This chapter discusses the time-related functionality provided by the C++ Standard Library, known collectively as the chrono library. It is a collection of classes and functions to work with time and dates. The library consists of the following components:

  • Durations
  • Clocks
  • Time points
  • Dates
  • Time zones

Everything is defined in <chrono> in the std::chrono namespace. However, before we can start the discussion of each of these chrono library components, we need a small digression to look at the compile-time rational number support available in C++, as this is heavily used by the chrono library.

The Ratio library allows you to exactly represent any finite rational number that you can use at compile time. Everything is defined in <ratio> and is in the std namespace. The numerator and denominator of a rational number are represented as compile-time constants of type std::intmax_t, which is a signed integer type with the maximum width supported by a compiler. Because of the compile-time nature of these rational numbers, using them might look a bit different than what you are used to. You cannot define a ratio object the same way as you define normal objects, and you cannot call member functions on it. Instead, ratio is a class template, and a specific instantiation of the ratio class template represents one specific rational number. To name such specific instantiations, you can use type aliases. For example, the following defines a compile-time rational number representing the fraction 1/60:

using r1 = ratio<1, 60>;

The numerator (num) and denominator (den) of the r1 rational number are compile-time constants and can be accessed as follows:

intmax_t num { r1::num };
intmax_t den { r1::den };

Remember that a ratio represents a compile-time rational number, which means that the numerator and denominator need to be known at compile time. The following generates a compilation error:

intmax_t n { 1 }; // Numerator
intmax_t d { 60 }; // Denominator
using r1 = ratio<n, d>; // Error

Making n and d constants works fine:

const intmax_t n { 1 }; // Numerator
const intmax_t d { 60 }; // Denominator
using r1 = ratio<n, d>; // Ok

Rational numbers are always normalized. For a rational number ratio<n, d>, the greatest common divisor, gcd, is calculated, and the numerator, num, and denominator, den, are then defined as follows:

  • num = sign(n)*sign(d)*abs(n)/gcd
  • den = abs(d)/gcd

The library supports adding, subtracting, multiplying, and dividing rational numbers. However, you cannot use the standard arithmetic operators because all these operations are again not happening on objects but on types, i.e., instantiations of the ratio class template, at compile time. Instead, you need to use specific arithmetic ratio class templates. The following arithmetic class templates are available: ratio_add, ratio_subtract, ratio_multiply, and ratio_divide, which perform addition, subtraction, multiplication, and division, respectively. These templates calculate the result as a new ratio type. This type can be accessed with the embedded type alias called type. For example, the following code first defines two ratios, one representing 1/60 and the other representing 1/30. The ratio_add template adds those two rational numbers together to produce the result rational number, which, after normalization, is 1/20:

using r1 = ratio<1, 60>;
using r2 = ratio<1, 30>;
using result = ratio_add<r1, r2>::type;

The standard also defines a number of ratio comparison class templates: ratio_equal, ratio_not_equal, ratio_less, ratio_less_equal, ratio_greater, and ratio_greater_equal. Just like the arithmetic ratio class templates, the ratio comparison class templates are all evaluated at compile time, again not on objects but on ratio types. These comparison templates define a new type that is an std::bool_constant, representing the result. bool_constant is an std::integral_constant, a struct template that stores a type and a compile-time constant value. For example, integral_constant<int, 15> stores an integer with value 15. bool_constant is an integral_constant with type bool. For instance, bool_constant<true> is integral_constant<bool, true>, which stores a Boolean with value true. The result of the ratio comparison templates is either bool_constant<true> or bool_constant<false>. The value associated with a bool_constant or an integral_constant can be accessed using the value data member. The following example demonstrates the use of ratio_less:

using r1 = ratio<1, 60>;
using r2 = ratio<1, 30>;
using res = ratio_less<r2, r1>;
println("{}", res::value); // false

The following code snippet combines everything just covered. Because ratios are not objects but types, you cannot do something like println("{}", r1); you need to get the numerator and denominator and print them separately.

// Define a compile-time rational number.
using r1 = ratio<1, 60>;
// Get numerator and denominator.
intmax_t num { r1::num };
intmax_t den { r1::den };
println("1) r1 = {}/{}", num, den);
// Add two rational numbers.
using r2 = ratio<1, 30>;
println("2) r2 = {}/{}", r2::num, r2::den);
using result = ratio_add<r1, r2>::type;
println("3) sum = {}/{}", result::num, result::den);
// Compare two rational numbers.
using res = ratio_less<r2, r1>;
println("4) r2 < r1: {}", res::value);

The output is as follows:

1) r1 = 1/60
2) r2 = 1/30
3) sum = 1/20
4) r2 < r1: false

The library provides a number of SI (Système International) type aliases for your convenience. They are as follows:

using yocto = ratio<1, 1'000'000'000'000'000'000'000'000>; // *
using zepto = ratio<1, 1'000'000'000'000'000'000'000>; // *
using atto = ratio<1, 1'000'000'000'000'000'000>;
using femto = ratio<1, 1'000'000'000'000'000>;
using pico = ratio<1, 1'000'000'000'000>;
using nano = ratio<1, 1'000'000'000>;
using micro = ratio<1, 1'000'000>;
using milli = ratio<1, 1'000>;
using centi = ratio<1, 100>;
using deci = ratio<1, 10>;
using deca = ratio<10, 1>;
using hecto = ratio<100, 1>;
using kilo = ratio<1'000, 1>;
using mega = ratio<1'000'000, 1>;
using giga = ratio<1'000'000'000, 1>;
using tera = ratio<1'000'000'000'000, 1>;
using peta = ratio<1'000'000'000'000'000, 1>;
using exa = ratio<1'000'000'000'000'000'000, 1>;
using zetta = ratio<1'000'000'000'000'000'000'000, 1>; // *
using yotta = ratio<1'000'000'000'000'000'000'000'000, 1>; // *

The SI units with an asterisk at the end are defined only if your compiler can represent the constant numerator and denominator values for those type aliases as an intmax_t. An example of how to use these predefined SI units is given during the discussion of durations in the next section.

A duration is an interval between two points in time. It is represented by the duration class template, which stores a number of ticks and a tick period. The tick period is the time in seconds between two ticks and is represented as a compile-time ratio constant, which means it could be a fraction of a second. Ratios are discussed in the previous section. The duration template accepts two template type parameters and is defined as follows:

template <class Rep, class Period = ratio<1>> class duration {}

The first template parameter, Rep, is the type of variable storing the number of ticks and should be an arithmetic type, for example long, double, and so on. The second template parameter, Period, is the rational constant representing the period of a tick. If you don’t specify the tick period, the default value ratio<1> is used, which represents a tick period of one second.

Three constructors are provided: the default constructor; one that accepts a single value, the number of ticks; and one that accepts another duration. The latter constructor can be used to convert from one duration to another duration, for example, from minutes to seconds. An example is given later in this section.

Durations support arithmetic operations such as +, -, *, /, %, ++, --, +=, -=, *=, /=, and %=, and they support the comparison operators == and <=>. The class also contains the member functions shown in the following table:

MEMBER FUNCTIONDESCRIPTION
Rep count() constReturns the duration value as the number of ticks. The return type is the type specified as the first template type parameter for the duration template.
static duration zero()Returns a duration with a duration value equivalent to zero.
static duration min()static duration max()Returns a duration with the minimum/maximum possible duration value representable by the type specified as the first template type parameter for the duration template.

The library supports floor(), ceil(), round(), and abs() operations on durations that behave just as they behave with numerical data.

Let’s now see how durations can be defined. A duration where each tick is one second can be defined as follows:

duration<long> d1;

Because ratio<1> is the default tick period, this is the same as writing:

duration<long, ratio<1>> d1;

The next statement defines a duration in minutes (tick period = 60 seconds):

duration<long, ratio<60>> d2;

Here is a duration where each tick period is a sixtieth of a second:

duration<double, ratio<1, 60>> d3;

As you saw earlier in this chapter, <ratio> defines a number of SI rational constants. These predefined constants come in handy for defining tick periods. For example, the next statement defines a duration where each tick period is one millisecond:

duration<long long, milli> d4;

Let’s see durations in action. The following example demonstrates several aspects of durations. It shows you how to define them, how to perform arithmetic operations on them, how to print them to the screen, and how to convert one duration to another duration with a different tick period:

// Specify a duration where each tick is 60 seconds.
duration<long, ratio<60>> d1 { 123 };
println("{} ({})", d1, d1.count());
// Specify a duration represented by a double with each tick
// equal to 1 second and assign the largest possible duration to it.
auto d2 { duration<double>::max() };
println("{}", d2);
// Define 2 durations:
// For the first duration, each tick is 1 minute.
// For the second duration, each tick is 1 second.
duration<long, ratio<60>> d3 { 10 }; // = 10 minutes
duration<long, ratio<1>> d4 { 14 }; // = 14 seconds
// Compare both durations.
if (d3 > d4) { println("d3 > d4"); }
else { println("d3 <= d4"); }
// Increment d4 with 1 resulting in 15 seconds.
++d4;
// Multiply d4 by 2 resulting in 30 seconds.
d4 *= 2;
// Add both durations and store as minutes.
duration<double, ratio<60>> d5 { d3 + d4 };
// Add both durations and store as seconds.
duration<long, ratio<1>> d6 { d3 + d4 };
println("{} + {} = {} or {}", d3, d4, d5, d6);
// Create a duration of 30 seconds.
duration<long> d7 { 30 };
// Convert the seconds of d7 to minutes.
duration<double, ratio<60>> d8 { d7 };
println("{} = {}", d7, d8);
println("{} seconds = {} minutes", d7.count(), d8.count());

The output is as follows:

123min (123)
1.79769e+308s
d3 > d4
10min + 30s = 10.5min or 630s
30s = 0.5min
30 seconds = 0.5 minutes

Pay special attention to the following two lines of code:

duration<double, ratio<60>> d5 { d3 + d4 };
duration<long, ratio<1>> d6 { d3 + d4 };

They both calculate d3+d4, with d3 given in minutes and d4 in seconds, but the first statement stores it as a floating-point value representing minutes, while the second statement stores the result as an integral value representing seconds. Conversion from minutes to seconds, or vice versa, happens automatically.

The following two lines from the example demonstrate how to explicitly convert between different units of time:

duration<long> d7 { 30 }; // seconds
duration<double, ratio<60>> d8 { d7 }; // minutes

The first statement defines a duration representing 30 seconds. The second statement converts these 30 seconds into minutes, resulting in 0.5 minutes. Converting in this direction can result in a non-integral value and thus requires you to use a duration represented by a floating-point type; otherwise, you will get some cryptic compilation errors. The following statements, for example, do not compile because d8 is using long instead of a floating-point type:

duration<long> d7 { 30 }; // seconds
duration<long, ratio<60>> d8 { d7 }; // minutes // Error!

You can, however, force this conversion by using duration_cast():

duration<long> d7 { 30 }; // seconds
auto d8 { duration_cast<duration<long, ratio<60>>>(d7) }; // minutes

In this case, d8 will be 0 minutes, because integer division is used to convert 30 seconds to minutes.

Converting in the other direction does not require floating-point types if the source is an integral type, because the result is always an integral value if you started with an integral value. For example, the following statements convert ten minutes into seconds, both represented by the integral type long:

duration<long, ratio<60>> d9 { 10 }; // minutes
duration<long> d10 { d9 }; // seconds

The library provides the following standard duration types in the std::chrono namespace:

using nanoseconds = duration<X 64 bits, nano>;
using microseconds = duration<X 55 bits, micro>;
using milliseconds = duration<X 45 bits, milli>;
using seconds = duration<X 35 bits>;
using minutes = duration<X 29 bits, ratio<60>>;
using hours = duration<X 23 bits, ratio<3'600>>;
using days = duration<X 25 bits, ratio_multiply<ratio<24>, hours::period>>;
using weeks = duration<X 22 bits, ratio_multiply<ratio<7>, days::period>>;
using years = duration<X 17 bits,
ratio_multiply<ratio<146'097, 400>, days::period>>;
using months = duration<X 20 bits, ratio_divide<years::period, ratio<12>>>;

The exact type of X depends on your compiler, but the C++ standard requires it to be a signed integer type of at least the specified size. The preceding type aliases make use of the predefined SI ratio type aliases that are described earlier in this chapter. With these predefined types, instead of writing this:

duration<long, ratio<60>> d9 { 10 }; // minutes

you can simply write this:

minutes d9 { 10 }; // minutes

The following code is another example of how to use these predefined durations. The code first defines a variable t, which is the result of 1 hour + 23 minutes + 45 seconds. The auto keyword is used to let the compiler automatically figure out the exact type of t. The second statement uses the constructor of the predefined seconds duration to convert the value of t to seconds and writes the result to the console:

auto t { hours { 1 } + minutes { 23 } + seconds { 45 } };
println("{}", seconds { t });

Because the standard requires that the predefined durations use integer types, there can be compilation errors if a conversion could end up with a non-integral value. While integer division normally truncates, in the case of durations, which are implemented with ratio types, the compiler declares any computation that could result in a non-zero remainder as a compile-time error. For example, the following code does not compile because converting 90 seconds to minutes results in 1.5 minutes:

seconds s { 90 };
minutes m { s };

However, the following code does not compile either, even though 60 seconds is exactly 1 minute. It is flagged as a compile-time error because converting from seconds to minutes could result in non-integral values:

seconds s { 60 };
minutes m { s };

Converting in the other direction works perfectly fine because the minutes duration uses an integral type, and converting it to seconds always results in an integral value:

minutes m { 2 };
seconds s { m };

You can use the standard literals h, min, s, ms, us, and ns for creating durations. Technically, these are defined in the std::literals::chrono_literals namespace, but just as for the standard string literals discussed in Chapter 2, “Working with Strings and String Views,” the chrono_literals namespace is an inline namespace. So, you can make the chrono literals available with any of the following using directives:

using namespace std;
using namespace std::literals;
using namespace std::chrono_literals;
using namespace std::literals::chrono_literals;

Additionally, the literals are also made available in the std::chrono namespace. Here is an example:

using namespace std::chrono;
// …
auto myDuration { 42min }; // 42 minutes

The chrono library provides the hh_mm_ss class template, which accepts a Duration and splits the given duration into hours, minutes, seconds, and subseconds. It has getters hours(), minutes(), seconds(), and subseconds() to retrieve the data, always returning non-negative values. The is_negative() member function returns true if the duration is a negative duration, false otherwise. You’ll use the hh_mm_ss class template in one of the exercises at the end of this chapter.

A clock is a class consisting of a time_point and a duration. The time_point type is discussed in detail in the next section, but those details are not required to understand how clocks work. However, time_points themselves depend on clocks, so it’s important to know the details of clocks first.

The standard defines several clocks, which are described in the following table. The epoch of a clock is the time at which the clock starts counting.

CLOCKDESCRIPTIONEPOCH
system_clockRepresents the UTC wall clock time from the system-wide real-time clock.1970-01-01 00:00:00
steady_clockGuarantees its time_point never decreases, which is not guaranteed for system_clock because the system clock can be adjusted at any time. In fact, this clock is not required to be related to wall clock time; e.g., it could be the time since the start of the operating system.Unspecified
high_resolution_clockHas the shortest possible tick period. Depending on your compiler, it is possible for this clock to be a synonym for steady_clock or system_clock.Unspecified
utc_clockRepresents the Coordinated Universal Time (UTC) wall clock time.1970-01-01 00:00:00
tai_clockRepresents International Atomic Time (TAI), using a weighted average of several atomic clocks.1958-01-01 00:00:00
gps_clockRepresents Global Position System (GPS) time, i.e., the time maintained by the atomic clocks of GPS satellites.1980-01-06 00:00:00
file_clockRepresents file time. It’s an alias for std::filesystem::file_time_type.Unspecified, but typically 1970-01-01 on Unix, and 1601-01-01 on Windows.

The utc_clock is the only clock that tracks leap seconds, which are seconds that are occasionally added to or subtracted from UTC time to correct for any mismatch between UTC time and true solar time. The other clocks don’t track leap seconds, while for file_clock it’s unspecified.

Every clock has a static now() member function to get the current time as a time_point, and an is_steady() member function returning true if the clock is steady, i.e., never goes backwards, false otherwise.

The system_clock also defines two static helper member functions for converting time_points to and from the time_t C-style time representation. The first one is called to_time_t(), and it converts a given time_point to a time_t; the second one, from_time_t(), performs the opposite conversion. The time_t type is defined in <ctime>.

The following example demonstrates how to get the current UTC time and print it to the console in a human-readable format:

// Set the global locale to the user's local (see Chapter 21).
locale::global(locale { "" });
// Print the current UTC time.
println("UTC: {:L}", system_clock::now());
println("UTC: {:L%c}", system_clock::now());

This code snippet first sets the global locale to the user’s locale; see Chapter 21, “String Localization and Regular Expressions.” This makes sure everything is printed according to the user’s preferences. The println() statements use the L format specifier to format the date and time according to the configured global locale. The effect of the %c format specifier is also demonstrated. There are many more format specifiers supported. Consult a Standard Library reference to learn more about them. Here is sample output from the previous code snippet:

UTC: 2023-07-19 11:38:44,5521944
UTC: 2023-07-19 11:38:44

To time how long it takes for a piece of code to execute, you want to use a clock that is guaranteed not to go backwards. Hence, you should use a steady_clock. The following code snippet gives an example. The actual type of the variables start and end is steady_clock::time_point, and the actual type of diff is a duration.

// Get the start time.
auto start { steady_clock::now() };
// Execute code that you want to time.
const int numberOfIterations { 10'000'000 };
double d { 0 };
for (int i { 0 }; i < numberOfIterations; ++i) { d += sqrt(abs(sin(i) * cos(i))); }
// Get the end time and calculate the difference.
auto end { steady_clock::now() };
auto diff { end - start };
// Use the calculated result, otherwise the compiler might
// optimize away the entire loop!
println("d = {}", d);
// Convert the difference into milliseconds and output to the console.
println("Total: {}", duration<double, milli> { diff });
// Use duration_cast() if you don't need fractional milliseconds.
println("Total: {}", duration_cast<milliseconds>(diff));
// Print the time per iteration in nanoseconds.
println("{} per iteration", duration<double, nano> { diff / numberOfIterations });

Here is the output running on my test system:

d = 5393526.082683575
Total: 78.7931ms
Total: 78ms
7ns per iteration

The loop in this example is performing some arithmetic operations with sqrt(), abs(), sin(), and cos() to make sure the loop doesn’t end too fast. If you get really small values for the difference in milliseconds on your system, those values will not be accurate, and you should increase the number of iterations of the loop to make it last longer. Small timings will not be accurate because while timers often have a resolution in milliseconds, on most operating systems, this timer is updated infrequently, for example, every 10 ms or 15 ms. This induces a phenomenon called gating error, where any event that occurs in less than one timer tick appears to take place in zero units of time, and any event between one and two timer ticks appears to take place in one timer unit. For example, on a system with a 15 ms timer update, a loop that takes 44 ms will appear to take only 30 ms. When using such timers to time computations, it is important to make sure that the entire computation takes place across a fairly large number of basic timer tick units so that these errors are minimized.

A point in time is represented by the time_point class and stored as a duration relative to an epoch, representing the beginning of time. A time_point is always associated with a certain clock, and the epoch is the origin of this associated clock. For example, the epoch for the classic Unix/Linux time is January 1, 1970, and durations are measured in seconds. The epoch for Windows is January 1, 1601, and durations are measured in 100-nanosecond units. Other operating systems have different epoch dates and duration units.

The time_point class has a function called time_since:epoch(), which returns a duration representing the time between the epoch of the associated clock and the stored point in time.

Arithmetic operations of time_points and durations that make sense are supported. The following table lists those operations. tp is a time_point, and d is a duration:

tp + d = tptp – d = tp
d + tp = tptp – tp = d
tp += dtp -= d

An example of an operation that is not supported is tp+tp.

Comparison operators == and <=> to compare two time points are supported. Two static member functions are provided: min() and max() returning the smallest and largest possible point in time, respectively.

The time_point class has three constructors:

  • time_point(): Constructs a time_point initialized with duration::zero(). The resulting time_point represents the epoch of the associated clock.
  • time_point(const duration& d): Constructs a time_point initialized with the given duration. The resulting time_point represents epoch + d.
  • template<class Duration2> time_point(const time_point<clock, Duration2>& t): Constructs a time_point initialized with t.time_since:epoch().

Each time_point is associated with a clock. To create a time_point, you specify the clock as the template parameter:

time_point<steady_clock> tp1;

Each clock also knows its time_point type, so you can also write it as follows:

steady_clock::time_point tp1;

The following code snippet demonstrates some operations with time_points:

// Create a time_point representing the epoch of the associated steady clock.
time_point<steady_clock> tp1;
// Add 10 minutes to the time_point.
tp1 += minutes { 10 };
// Store the duration between epoch and time_point.
auto d1 { tp1.time_since:epoch() };
// Convert the duration to seconds and output to the console.
duration<double> d2 { d1 };
println("{}", d2);

The output is as follows:

600s

Converting time_points can be done implicitly or explicitly, similar to duration conversions. Here is an example of an implicit conversion. The output is 42000ms:

time_point<steady_clock, seconds> tpSeconds { 42s };
// Convert seconds to milliseconds implicitly.
time_point<steady_clock, milliseconds> tpMilliseconds { tpSeconds };
println("{}", tpMilliseconds.time_since:epoch());

If the implicit conversion can result in a loss of data, then you need an explicit conversion using time_point_cast(), similar to using duration_cast() for explicit duration casts as discussed earlier in this chapter. The following example outputs 42000ms, even though you start from 42,424ms:

time_point<steady_clock, milliseconds> tpMilliseconds { 42'424ms };
// Convert milliseconds to seconds explicitly.
time_point<steady_clock, seconds> tpSeconds {
time_point_cast<seconds>(tpMilliseconds) };
// Or:
// auto tpSeconds { time_point_cast<seconds>(tpMilliseconds) };
// Convert seconds back to milliseconds and output the result.
milliseconds ms { tpSeconds.time_since:epoch() };
println("{}", ms);

The library supports floor(), ceil(), and round() operations for time_points that behave just as they behave with numerical data.

The Standard Library supports working with calendar dates. At this moment, only the Gregorian calendar is supported, but if need be, you can always implement your own calendars that can interoperate with the rest of the <chrono> functionality, such as Coptic and Julian calendars.

The Standard Library provides quite a few classes and functions to work with dates (and time zones discussed in a later section). This text discusses the most important classes and functions. Consult a Standard Library reference (see Appendix B, “Annotated Bibliography”) to get a complete overview of everything that’s available.

The following calendrical classes are available to create dates, all defined in std::chrono:

CLASSDESCRIPTION
yearRepresents a year in the range [-32767, 32767]. A year has a member function called is_leap() returning true if a given year is a leap year, false otherwise. min() and max() static member functions return the minimum and maximum year, respectively.
monthRepresents a month in the range [1, 12]. Additionally, there are 12 named constants provided for the 12 months, for example: std::chrono::January.
dayRepresents a day in the range [1, 31].
weekdayRepresents a day of the week in the range [0, 6], where 0 means Sunday. Additionally, there are seven named constants provided for the seven weekdays, for example: std::chrono::Sunday.
weekday_indexedRepresents the first, second, third, fourth, or fifth weekday of a month. Can easily be constructed from a weekday, for example: Monday[2] is the second Monday of a month.
weekday_lastRepresents the last weekday of some month.
month_dayRepresents a specific month and day.
month_day_lastRepresents the last day of a specific month.
month_weekdayRepresents the nth weekday of a specific month.
month_weekday_lastRepresents the last weekday of a specific month.
year_monthRepresents a specific year and month.
year_month_dayRepresents a specific year, month, and day.
year_month_day_lastRepresents the last day of a specific year and month.
year_month_weekdayRepresents the nth weekday of a specific year and month.
year_month_weekday_lastRepresents the last weekday of a specific year and month.

All of these classes have a member function called ok() that returns true if the given object is in a valid range, false otherwise. Two additional standard literals are provided in std::literals::chrono_literals: y to create years, and d to create days. Complete dates can be constructed using operator/ to specify year, month, and day, in three orders: Y/M/D, M/D/Y, D/M/Y. Here are some examples to create dates:

year y1 { 2020 };
auto y2 { 2020y };
month m1 { 6 };
auto m2 { June };
day d1 { 22 };
auto d2 { 22d };
// Create a date for 2020-06-22.
year_month_day fulldate1 { 2020y, June, 22d };
auto fulldate2 { 2020y / June / 22d };
auto fulldate3 { 22d / June / 2020y };
// Create a date for the 3rd Monday of June 2020.
year_month_day fulldate4 { Monday[3] / June / 2020 };
// Create a month_day for June 22 of an unspecified year.
auto june22 { June / 22d };
// Create a year_month_day for June 22, 2020.
auto june22_2020 { 2020y / june22 };
// Create a month_day_last for the last day of a June of an unspecified year.
auto lastDayOfAJune { June / last };
// Create a year_month_day_last for the last day of June for the year 2020.
auto lastDayOfJune2020 { 2020y / lastDayOfAJune };
// Create a year_month_weekday_last for the last Monday of June 2020.
auto lastMondayOfJune2020 { 2020y / June / Monday[last] };

sys_time is a type alias for a time_point of a system_clock with a certain duration. It’s defined as follows:

template <typename Duration>
using sys_time = std::chrono::time_point<std::chrono::system_clock, Duration>;

Based on the sys_time type alias, two additional type aliases are defined to represent a sys_time with a precision of seconds, and one with a precision of days:

using sys_seconds = sys_time<std::chrono::seconds>;
using sys_days = sys_time<std::chrono::days>;

sys_days, for example, represents the number of days since the system_clock epoch, and hence, it’s a serial-based type; that is, it just contains a single number (days since epoch). On the other hand, year_month_day, for example, is a field-based type, it stores a year, a month, and a day in separate fields. When doing a lot of arithmetic with dates, a serial-based type will be more performant than a field-based type.

Similar type aliases exist to work with local time: local_time, local_seconds, and local_days. These are demonstrated in a later section on time zones.

You can create a sys_days representing today as follows. floor() is used to truncate a time_point to a precision of days:

auto today { floor<days>(system_clock::now()) };

sys_days can be used to convert a year_month_day to a time_point, for example:

system_clock::time_point t1 { sys_days { 2020y / June / 22d } };

The opposite conversion, converting a time_point to a year_month_day, can be done with a year_month_day constructor. The following code snippet gives two examples:

year_month_day yearmonthday { floor<days>(t1) };
year_month_day today2 { floor<days>(system_clock::now()) };

A complete date including a time can be build up as well. Here’s an example:

// Full date with time: 2020-06-22 09:35:10 UTC.
auto t2 { sys_days { 2020y / June / 22d } + 9h + 35min + 10s };

Dates can be written to streams using the familiar insertion operator:

cout << yearmonthday << endl;

Printing and formatting dates are also supported. The L format specifier formats the output according to the current global locale.

println("{:L}", yearmonthday);

Keep in mind that the output might sometimes be unexpected. For example, lastMondayOfJune2020 is defined earlier as follows:

// Create a year_month_weekday_last for the last Monday of June 2020.
auto lastMondayOfJune2020 { 2020y / June / Monday[last] };

When you print this, the output is “2020/Jun/Mon[last]”:

println("{:L}", lastMondayOfJune2020); // 2020/Jun/Mon[last]

If you want to output the exact date, “2020-06-29,” then you need to convert the year_month_weekday_last to a year_month_day and then output the result:

year_month_day lastMondayOfJune2020YMD { sys_days { lastMondayOfJune2020 } };
println("{:L}", lastMondayOfJune2020YMD); // 2020-06-29

If a date is invalid, printing will insert an error. For example, the string “is not a valid date” is appended to an invalid year_month_day.

Using the L format specifier, names of days and months are correctly localized according to the current global locale. For example, the following code snippet first sets the global locale to Dutch, nl-NL, and then uses the L format specifier to print Monday in Dutch. The %A format specifier causes the full name to be printed instead of the abbreviated name. Consult your favorite Standard Library reference for a full list of all supported date format specifiers.

locale::global(locale { "nl-NL" });
println("Monday in Dutch is {:L%A}", Monday);

The output is as follows:

Monday in Dutch is maandag

You can perform arithmetic with dates. Here’s an example:

// Full date with time: 2020-06-22 09:35:10 UTC.
auto t2 { sys_days { 2020y / June / 22d } + 9h + 35min + 10s };
auto t3 { t2 + days { 5 } }; // Add 5 days to t2.
auto t4 { t3 + years { 1 } }; // Add 1 year to t3.

Be careful, though, as the result might not always be as expected. For example:

auto t5 { sys_days { 2020y / June / 22d } + 9h + 35min + 10s };
auto t6 { t5 + years { 1 } }; // Add 1 year to t5
println("t5 = {:L}", t5);
println("t6 = {:L}", t6);

The result is as follows:

t5 = 2020-06-22 09:35:10
t6 = 2021-06-22 15:24:22

In looking at the results, you can see that the year is updated, but you can also see that the time has changed. The issue here is that we are working with a serial type: sys_days is a time_point, which is a serial type. Adding 1 year to such a serial type does not add 86,400 * 365 = 31,536,000 seconds. Instead, the standard mandates that adding 1 year must add 1 average year to keep leap years into account, and hence, it must add 86,400 * ((365 * 400) + 97) / 400 = 31,556,952 seconds.

If you need to add exactly 1 year, then it’s best to use a field-based type instead, for example:

// Split t5 into days and remaining seconds.
sys_days t5_days { time_point_cast<days>(t5) };
seconds t5_seconds { t5 - t5_days };
// Convert the t5_days serial type to field-based type.
year_month_day t5_ymd { t5_days };
// Add 1 year.
year_month_day t7_ymd { t5_ymd + years { 1 } };
// Convert back to a serial type.
auto t7 { sys_days { t7_ymd } + t5_seconds };
println("t7 = {:L}", t7);

This results in:

t7 = 2021-06-22 09:35:10

To facilitate working with time zones, the C++ Standard Library contains a copy of the Internet Assigned Numbers Authority (IANA) time zone database (www.iana.org/time-zones). You can get access to this database by calling std::chrono::get_tzdb(), which returns a reference-to-const to a single existing instance of type std::chrono::tzdb. This database gives access to all known time zones through a public vector called zones. Each entry in this vector is a time_zone, which has a name, accessible with name(), and member functions to_sys() and to_local() to convert a local_time to a sys_time, and vice versa. Due to daylight saving time, it could be that a conversion from local_time to sys_time is either ambiguous or nonexistent. In such cases, the conversion throws an exception of type ambiguous_local_time or nonexistent_local_time, respectively.

Here is a code snippet listing all available time zones:

const auto& database { get_tzdb() };
for (const auto& timezone : database.zones) {
println("{}", timezone.name());
}

The std::chrono::locate_zone() function can be used to retrieve a time_zone based on its name and throws a runtime_error exception if the requested time zone cannot be found in the database. The current_zone() function can be used to get the current time zone. For example:

auto* brussels { locate_zone("Europe/Brussels") };
auto* gmt { locate_zone("GMT") };
auto* current { current_zone() };

time_zone instances can be used to convert times between different zones:

// Convert current time (UTC), to time in Brussels, and time in current zone.
auto nowUTC { system_clock::now() }; // In UTC.
auto nowInBrussels { brussels->to_local(nowUTC) }; // In Brussels' time zone.
auto nowInCurrentZone { current->to_local(nowUTC) }; // In current time zone.
println("Now UTC: {:L%c}", nowUTC);
println("Now Brussels: {:L%c}", nowInBrussels);
println("Now in current: {:L%c}", nowInCurrentZone);
// Construct a UTC time. (2020-06-22 09:35:10 UTC)
auto t { sys_days { 2020y / June / 22d } + 9h + 35min + 10s };
// Convert UTC time to Brussels' local time.
auto converted { brussels->to_local(t) };
println("Converted: {:L}", converted);

The zoned_time class is used to represent a time_point in a specific time_zone. The following snippet constructs a specific time in the Brussels’ time zone and then converts it to New York time:

// Construct a local time in the Brussels' time zone.
zoned_time<hours> brusselsTime{ brussels, local_days { 2020y / June / 22d } + 9h };
// Convert to New York time.
zoned_time<hours> newYorkTime { "America/New_York", brusselsTime };
println("Brussels: {:L}", brusselsTime.get_local_time());
println("New York: {:L}", newYorkTime.get_local_time());

This chapter discussed how to use the ratio class template to define and work with compile-time rational numbers. You also learned how to work with durations, clocks, time points, dates, and time zones provided by the C++ Standard Library through the chrono library.

The next chapter focusses on the functionality provided by the Standard Library to generate random numbers.

By solving the following exercises, you can practice the material discussed in this chapter. Solutions to all exercises are available with the code download on the book’s website at www.wiley.com/go/proc++6e. However, if you are stuck on an exercise, first reread parts of this chapter to try to find an answer yourself before looking at the solution from the website.

  1. Exercise 22-1: Let’s play a bit with durations. Create a duration, d1, with a precision of seconds, initialized to 42 seconds. Create a second duration, d2, with a precision of minutes, initialized to 1.5 minutes. Calculate the sum of d1 and d2. Write out the result to the standard output, once expressed in seconds, once expressed in minutes.
  2. Exercise 22-2: Ask the user to enter a date as yyyy-mm-dd, for example, 2020-06-22. Use a regular expression (see Chapter 21) to extract the year, month, and day components, and finally, use year_month_day to validate the date.
  3. Exercise 22-3: Write a getNumberOfDaysBetweenDates() function that calculates the number of days between two given dates. Test your implementation in your main() function.
  4. Exercise 22-4: Write a program that prints out the day of the week of June 22, 2020.
  5. Exercise 22-5: Construct a UTC time. Convert this time to the local time in Tokyo, Japan. Further convert the resulting time to New York time. And finally convert the resulting time to GMT. Verify that the original UTC time and the final GMT time are equal. Tip: The time zone identifier for Tokyo is Asia/Tokyo, for New York it is America/New_York, and for GMT it is GMT.
  6. Exercise 22-6: Write a getDurationSinceMidnight() function that returns the duration between midnight and the current local time in seconds. Use your function to print out the number of seconds since midnight to the standard output console. Finally, use the hh_mm_ss class to convert the duration returned by your function to hours, minutes, and seconds, and print the result on standard output.