Skip to content

Examples and Recipes

Howard Hinnant edited this page Aug 1, 2023 · 125 revisions

This page contains examples and recipes contributed by community members. Feel free to add your own contributions by clicking on the "Edit" button. Please "sign" your contributions by adding a link to your GitHub profile. But please understand that your contributions will henceforth be considered donated under the Creative Commons Attribution 4.0 International License. If this requirement is a problem for anyone, bring it to Howard's attention and we will try to work out a compromise.

Contents


The current local time

(by Howard Hinnant)

This couldn't be easier:

#include "date/tz.h"
#include <iostream>

int
main()
{
    std::cout << date::zoned_time{date::current_zone(),
                                  std::chrono::system_clock::now()} << '\n';
}

system_clock::now() of course gets the current time. It is a de facto standard that this current time is measured in terms of Unix Time which is a very close approximation to UTC. zoned_time pairs the current UTC time with the computer's current local time zone. The zoned_time has a streaming operator so you can easily print it out:

2016-07-05 23:01:05.818378 EDT

Note that the precision of this output is with whatever precision your system_clock::now() supports (microseconds on macOS where I'm writing this).

If you don't want the timezone abbreviation printed out, another option is to call the member function to_local on time_zone itself:

#include "date/tz.h"
#include <iostream>

int
main()
{
    using date::operator<<;
    std::cout << date::current_zone()->
                     to_local(std::chrono::system_clock::now()) << '\n';
}
2016-07-05 23:01:05.818378

The using date::operator<<; is necessary because the result of to_local is a std::chrono::time_point and ADL won't find it's streaming operator in namespace date without a little help.

The current time somewhere else

(by Howard Hinnant)

If you need to find out the current time where you aren't, then that is a simple matter too:

#include "date/tz.h"
#include <iostream>

int
main()
{
    std::cout << date::zoned_time{"Asia/Shanghai",
                                  std::chrono::system_clock::now()} << '\n';
}

This outputs the current time in Shanghai, for example:

2016-07-06 11:01:05.818378 CST

All IANA timezone names (or links -- aliases to timezones) are supported.

Like with the current local time, if you just want a local time_point (without the timezone abbreviation), you can work directly with the time_zone if desired:

#include "date/tz.h"
#include <iostream>

int
main()
{
    using date::operator<<;
    std::cout << date::locate_zone("Asia/Shanghai")->
                                   to_local(std::chrono::system_clock::now()) << '\n';
}
2016-07-06 11:01:05.818378

If locate_zone() can't find the time_zone, it will throw a std::runtime_error with a helpful what() message.

Get the current difference between any two arbitrary time zones

(by Howard Hinnant)

Would you just like to know how many hours ahead or behind your friend is in another time zone? Here's how you do that with this library.

#include "date/tz.h"
#include <iostream>

int
main()
{
    auto current_time = std::chrono::system_clock::now();
    auto la = date::zoned_time{"America/Los_Angeles", current_time};
    auto sy = date::zoned_time{"Australia/Sydney", current_time};
    std::cout << date::format("%T\n", sy.get_local_time() - la.get_local_time());
}

Let's say you want to find out how many hours ahead Sydney is from LA (now). First get the current UTC time. Then create a zoned_time for both "America/Los_Angeles" and "Australia/Sydney" using that current UTC time. Then subtract their local_times. format is a handy formatting tool for printing out that duration:

17:00:00.000000

Because we did not specify otherwise, the default behavior is to give us the full precision of system_clock::time_point. If you would prefer other behavior (e.g. minutes precision), that is easily accomplished with just a little more work:

std::cout << date::format("%H:%M\n", sy.get_local_time() - la.get_local_time());

And now the output is:

17:00

Set simultaneous meeting in two different time zones

(by Howard Hinnant)

Say you want to set up a video conference between New York and Moscow. This can be done in a few simple ways. First you need to decide when the meeting is going to be with respect to somebody's clock. For example, let's say we want to have the meeting on Jul 8, 2016 at 9am in New York. How do we define that time, and then find the same instant in Moscow?

#include "date/tz.h"
#include <iostream>

int
main()
{
    using namespace std::chrono;
    using namespace date;
    zoned_time ny{"America/New_York", local_days{July/8/2016} + 9h};
    zoned_time moscow{"Europe/Moscow", ny};
    std::cout << ny << '\n';
    std::cout << moscow << '\n';
}

The use of local_days creates a calendar date local to whatever time zone you pair it with (in this case "America/New_York"). To make this some time other than midnight, just add the time duration since midnight (hours, minutes, seconds, whatever) to the local_days, in this case 9h for 09:00:00. This forms a zoned_time that corresponds to 2016-07-08 09:00:00 EDT. To find the same time in Moscow, just create a new zoned_time with "Europe/Moscow" and the New York zoned_time. This creates a zoned_time with the equivalent UTC time, but associated with the time zone "Europe/Moscow".

Then just print it out:

2016-07-08 09:00:00 EDT
2016-07-08 16:00:00 MSK

Obviously you could just as easily specify the meeting in Moscow's time zone and then find the equivalent time in New York:

    zoned_time moscow{"Europe/Moscow", local_days{8_d/July/2016} + 16h};
    zoned_time ny{"America/New_York", moscow};

This would result in the exact same output. For those paying attention, I reordered the date from m/d/y to d/m/y just to show that I could. The meaning is the same.

Sometimes it is convenient to specify the time independent of either timezone. For example this might be some celestial event such as an eclipse. Often such events are recorded in UTC so that people in all time zones can more easily know the correct time. It is just as easy to use UTC for this example:

    auto utc = sys_days{2016_y/July/8} + 13h;
    zoned_time ny{"America/New_York", utc};
    zoned_time moscow{"Europe/Moscow", utc};

I reordered the date to y/m/d just to show that I could. As long as the first unit is unambiguous (year, month or day), the following two are unambiguous (only y/m/d, d/m/y and m/d/y are accepted; all others are rejected at compile time).

Instead of local_days, sys_days is used instead. sys_days means UTC (technically it means Unix Time which is a very close approximation to UTC). Then you can construct each zoned_time with the UTC time (which has type sys_time<hours> in this example). You could also construct the second zoned_time from the first, just as before:

    zoned_time ny{"America/New_York", sys_days{July/8/2016} + 13h};
    zoned_time moscow{"Europe/Moscow", ny};

In any event, the output is still:

2016-07-08 09:00:00 EDT
2016-07-08 16:00:00 MSK

Interfacing with the C API

(by Howard Hinnant)

The goal of this library is to ensure that you never have to bother with the C timing API again. That being said, the real world interrupts my goals sometimes, and one just has to convert to/from a tm, or timespec now and then for interfacing with somebody else's code that still uses the C timing API. So this note is about how to write those conversion functions:

Converting from a tm

C allows a tm to contain more members than are documented by the C specification. This example assumes only the portable members of tm. Hopefully once you understand how to work with the portable members, you will be able to extend your knowledge to the non-portable members for your platform if necessary. The portable tm is:

struct tm
{
    int tm_sec;   // seconds after the minute — [0, 60]
    int tm_min;   // minutes after the hour — [0, 59]
    int tm_hour;  // hours since midnight — [0, 23]
    int tm_mday;  // day of the month — [1, 31]
    int tm_mon;   // months since January — [0, 11]
    int tm_year;  // years since 1900
    int tm_wday;  // days since Sunday — [0, 6]
    int tm_yday;  // days since January 1 — [0, 365]
    int tm_isdst; // Daylight Saving Time flag
};

The tm struct can be used to hold a UTC time (sys_time or utc_time), or a local time (local_time). However the tm does not contain enough information to identify the time zone of a local time, or even the UTC offset (some platforms include a utc offset member as a conforming extension). Additionally the tm is limited to seconds precision. This limits what we can convert to. For example it is not possible to convert to a zoned_time because there is not enough information to do so. And it only makes sense to convert to a destination with seconds precision. If you want to convert to something with higher precision than that, it is an implicit conversion from the seconds-precision result of these functions.

So we can convert from a tm to either a sys_seconds, or a local_seconds. Here is the first:

date::sys_seconds
to_sys_time(std::tm const& t)
{
    using namespace date;
    using namespace std::chrono;
    return sys_days{year{t.tm_year + 1900}/(t.tm_mon+1)/t.tm_mday} +
           hours{t.tm_hour} + minutes{t.tm_min} + seconds{t.tm_sec};
}

As can be seen, not all of the fields of the tm are needed for this conversion. And this conversion assumes that the tm represents a UTC time point. The conversion itself is quite straightforward: Convert the year/month/day information into a sys_days, and then add the H:M:S information, converting into chrono durations along the way. One has to be careful with the year and month data from tm to bias it correctly.

Converting to a local_seconds is identical except for the use of local_days in place of sys_days:

date::local_seconds
to_local_time(std::tm const& t)
{
    using namespace date;
    using namespace std::chrono;
    return local_days{year{t.tm_year + 1900}/(t.tm_mon+1)/t.tm_mday} +
           hours{t.tm_hour} + minutes{t.tm_min} + seconds{t.tm_sec};
}

This can give you a local time in an as-yet-unspecified time zone. One can subsequently pair that local_time with a time zone such as current_zone() to complete the conversion (assuming current_zone() is the correct time zone).

Converting to a tm

Conversion to a tm is a bit more flexible since we generally have more than enough information to fill out the tm. Let's start with converting from zoned_seconds to a tm:

std::tm
to_tm(date::zoned_seconds tp)
{
    using namespace date;
    using namespace std;
    using namespace std::chrono;
    auto lt = tp.get_local_time();
    auto ld = floor<days>(lt);
    time_of_day<seconds> tod{lt - ld};  // <seconds> can be omitted in C++17
    year_month_day ymd{ld};
    tm t{};
    t.tm_sec  = tod.seconds().count();
    t.tm_min  = tod.minutes().count();
    t.tm_hour = tod.hours().count();
    t.tm_mday = (ymd.day() - 0_d).count();
    t.tm_mon  = (ymd.month() - January).count();
    t.tm_year = (ymd.year() - 1900_y).count();
    t.tm_wday = (weekday{ld} - Sunday).count();
    t.tm_yday = (ld - local_days{ymd.year()/January/1}).count();
    t.tm_isdst = tp.get_info().save != minutes{0};
    return t;
}

A presumption here is that one desires to convert the local time in the zoned_time to the tm. If that assumption is not the case, we cover putting a UTC time into a tm below. So the first thing to do is to get the local time out of the zoned_seconds with tp.get_local_time().

Next we truncate the local_seconds into local_days using the floor<days>(lt) function. Now we have two local_time time_points, one with a precision of seconds, and the other with a precision of days. The days-precision time_point can be explicitly converted to a year_month_day object so that we can retrieve the year, month and day fields.

The time of day is just the difference between the seconds-precision time_point and the days-precision time_point, which gives us the seconds since midnight. This duration can be broken down into a {hours, minutes, seconds} struct by converting it to a time_of_day<seconds>. In C++17 the <seconds> template parameter will be deduced by the seconds-presion duration used in the constructor.

Now we just start filling out the tm, being careful to bias the month and year correctly. It is also good practice to first zero the entire tm so as to zero-out any platform-specific fields of the tm.

The computation for days-since-Jan 1 is found by simply subtracting the expression for New Years day for the current year from the already stored local_days value.

Finally we can set tm_isdst to 1 if the save member of sys_info is not 0min, and to 0 otherwise. The sys_info can be obtained from the zoned_seconds, and contains all kinds of useful information (including the UTC offset should you want to install that into your platform-specific tm).

If we want to convert from a sys_seconds to a tm, that is quite easy to do using the conversion function above:

std::tm
to_tm(date::sys_seconds tp)
{
    return to_tm(date::zoned_seconds{tp});
}

This creates a zoned_time, and defaults the time_zone to "UTC", then passes that zoned_time to to_tm. If desired, one could repeat the code from zoned_seconds instead of reuse it. This would save a small amount of processing time involved in looking up "UTC" in the database. And in this event you would always set t.tm_isdst to 0. One would also use sys_days in place of local_days in this alternative.

One can also create a tm from local_seconds:

std::tm
to_tm(date::local_seconds tp)
{
    auto tm = to_tm(date::sys_seconds{tp.time_since_epoch()});
    tm.tm_isdst = -1;
    return tm;
}

In this variant, the time_zone is unknown, and thus -1 is the proper value for tm.tm_isdst.

Converting from a timespec

timespec is new in the latest C specification. It contains at least the following members in any order:

struct timespec
{
    time_t tv_sec;  // whole seconds -- >= 0
    long   tv_nsec; // nanoseconds -- [0, 999999999]
};

C uses timespec as both a time point, and as a time duration. So we should be able to convert to both nanoseconds (a duration), and sys_time<nanoseconds> (a time_point). Both are easy. First to convert to a duration:

std::chrono::nanoseconds
to_nanoseconds(timespec const& ts)
{
    using namespace std::chrono;
    return seconds{ts.tv_sec} + nanoseconds{ts.tv_nsec};
}

One just converts the integrals to their proper duration types and adds them. The result has type nanoseconds.

We can reuse the above the above function to convert to a time_point:

date::sys_time<std::chrono::nanoseconds>
to_time_point(timespec const& ts)
{
    return date::sys_time<std::chrono::nanoseconds>{to_nanoseconds(ts)};
}

Just get the duration and explicitly convert it to the proper time_point.

Converting to a timespec

The reverse conversions are only slightly more complex:

timespec
to_timespec(std::chrono::nanoseconds const& d)
{
    using namespace std::chrono;
    timespec ts;
    seconds s = duration_cast<seconds>(d);
    ts.tv_sec = s.count();
    ts.tv_nsec = (d - s).count();
    return ts;
}

First truncate the nanoseconds-precision duration to seconds-precision. That truncated value can be placed into ts.tv_sec. Now the difference between the original nanoseconds-precision duration and the seconds-precsion duration is the amount of a nanoseconds left over, and is assigned to ts.tv_nsec.

The conversion to a time_point follow exactly the same logic, with the syntax being slightly modified to account for the fact that we're working with time_points instead of durations:

timespec
to_timespec(date::sys_time<std::chrono::nanoseconds> const& tp)
{
    using namespace std::chrono;
    timespec ts;
    auto tps = time_point_cast<seconds>(tp);
    ts.tv_sec = tps.time_since_epoch().count();
    ts.tv_nsec = (tp - tps).count();
    return ts;
}

Get milliseconds since the local midnight

(by Howard Hinnant)

After reading this stack Overflow question/answers I decided it would be good to show how to solve this problem using this library. As the question is not crystal clear what it is asking, I will attempt to create an unambiguous problem statement here:

Find the time duration in milliseconds since the last midnight in the local time zone.

So that I can more easily test the code to do this, I'm going to write this more generally as:

    std::chrono::milliseconds
    since_local_midnight(std::chrono::system_clock::time_point t, const date::time_zone* zone);

And then create an overload to pass in the current time and current time zone:

    inline
    std::chrono::milliseconds
    since_local_midnight()
    {
        return since_local_midnight(std::chrono::system_clock::now(), date::current_zone());
    }

That way I can test things like times just after known daylight saving time transitions to make sure that my code is doing what I want it to.

This only takes a few lines of code:

std::chrono::milliseconds
since_local_midnight(std::chrono::system_clock::time_point t, const date::time_zone* zone)
{
    using namespace date;
    using namespace std::chrono;
    zoned_time zt{zone, t};
    zt = floor<days>(zt.get_local_time());
    return floor<milliseconds>(t - zt.get_sys_time());
}

The first thing to do is create a zoned_time which really does nothing at all but pair zone and t. This pairing is mainly just to make the syntax nicer.

The next step is to get the local time associated with t. That is what zt.get_local_time() does. This will have whatever precision t has, unless t is coarser than seconds, in which case the local time will have a precision of seconds.

The call to floor<days> truncates the local time to a precision of days. This effectively creates a local_time equal to the local midnight. By assigning this local_time back to zt, we don't change the time zone of zt at all, but we change the local_time of zt to midnight (and thus change its sys_time as well).

We can get the corresponding sys_time out of zt with zt.get_sys_time(). This is the UTC time which corresponds to the local midnight. It is then an easy process to subtract this from the input t and truncate the results to the desired precision.

As a further testing aid, it is convenient to write a since_local_midnight that takes a zoned_seconds (which is a zoned_time with the precision of seconds) that calls into the main overload:

inline
std::chrono::milliseconds
since_local_midnight(const date::zoned_seconds& zt)
{
    return since_local_midnight(zt.get_sys_time(), zt.get_time_zone());
}

So to output the current time in milliseconds since the local midnight, you would just:

std::cout << since_local_midnight() << '\n';

To ensure that our function is working, it is worthwhile to output a few example dates. This is most easily done by specifying a time zone (I'll use "America/New_York"), and some local date/times where I know the right answer:

zoned_time zt{locate_zone("America/New_York"), local_days{January/15/2016} + 3h};
std::cout << zt << " is " << since_local_midnight(zt) << " after midnight\n";

This 3am in the middle of the Winter. This outputs:

2016-01-15 03:00:00 EST is 10800000ms after midnight

which is correct (10800000ms == 3h).

I can run the test again just by assigning a new local time to zt. The following is 3am just after the "spring forward" daylight saving transition (2nd Sunday in March):

zt = local_days{Sunday[2]/March/2016} + 3h;
std::cout << zt << " is " << since_local_midnight(zt) << " after midnight\n";

This outputs:

2016-03-13 03:00:00 EDT is 7200000ms after midnight

Because the local time from 2am to 3am was skipped, this correctly outputs 2 hours since midnight.

An example from the middle of Summer gets us back to 3 hours after midnight:

zt = local_days{July/15/2016} + 3h;
std::cout << zt << " is " << since_local_midnight(zt) << " after midnight\n";
2016-07-15 03:00:00 EDT is 10800000ms after midnight

And finally an example just after the Fall transition from daylight saving back to standard gives us 4 hours:

zt = local_days{Sunday[1]/November/2016} + 3h;
std::cout << zt << " is " << since_local_midnight(zt) << " after midnight\n";
2016-11-06 03:00:00 EST is 14400000ms after midnight

Not only does this library make it easy to write the code to do the desired computation, it also makes it easy to write the test code.

What is my timezone database version?

(by Howard Hinnant)

Many people may not realize this, but the world's timezone rules are updated quite often. In the ten year period [2006 - 2015] the IANA timezone database was updated an average of 12.2 times a year (slightly more than once a month). The year 2015 was a slow one: updated only 7 times. But the 6th version for 2016 was released on July 5th, and events have already changed that will demand another version well before the end of the year.

A few of these updates are bug fixes. But most of them are the result of political changes on the ground: Governments around the world (when taken collectively) routinely change their timezone rules, and often with surprisingly little notice (as little as 3 days). So as computers talk to each other more and more, and trade timestamps, it becomes important for computers to ensure that they are using the same timezone rules so that round trip local timestamps are consistent.

This library can query the installed IANA timezone database for its version number. The version number is a std::string consisting of the year, followed by a lower case letter, with a signifying the first version for the year (there has yet to be more than 26 versions/year, which would be rather absurd).

Here is how you get the version of the installed IANA timezone database:

#include "date/tz.h"
#include <iostream>

int
main()
{
    std::cout << date::get_tzdb().version << '\n';
}

As I write this, the output is:

2016f

If the remote API is enabled (HAS_REMOTE_API == 1) then you can also query the latest version number at the IANA website with:

std::cout << date::remote_version() << '\n';

which currently outputs 2016f.

Obtaining a time_point from y/m/d h:m:s components

Date/time interpreted as UTC, to time_point

Converting a date/time, interpreted as UTC

struct DateTime {              // hold date/time (interpreted as UTC), to be converted to time_point
    int year;
    int month;
    int day;
    int hour = 0;
    int min  = 0;
    int sec  = 0;
};

DateTime datetime{2016 /*year*/,  12 /*month*/,  24 /*day*/,     23 /* hour */,  0 /* min */,  0 /*sec*/}; // variable definition

to a std::chrono::system_clock::time_point, can be done with the following function:

// convert date/time from UTC, to time_point
std::chrono::system_clock::time_point datetime_utc_to_timepoint(const DateTime &dt) // note: this function does NOT need the tz library and header
{
    using namespace std::chrono;
    using namespace date;

    auto ymd = year(dt.year)/dt.month/dt.day; // year_month_day type
    if (!ymd.ok()) { throw std::runtime_error("Invalid date"); }

    return                     sys_days(ymd) + hours(dt.hour) + minutes(dt.min) + seconds(dt.sec);
}

To print a std::chrono::system_clock::time_point, getting back a UTC date/time, the following can be used:

std::cout << "Date/Time is ";
date::operator<<(std::cout, tp) << std::endl;

// or 
using date::operator<<;
std::cout << "Date/Time is " << tp << std::endl;

// It will print the following
// Date/Time is 2016-12-24 23:00:00.000000000

The above needs only the "date/date.h" header. ("date/tz.h" header and tz library are not needed).

Date/time (interpreted as coming from a specific time-zone), to time_point

If the date/time is taken as being from a specific time-zone, then "date/tz.h" header and tz library are needed:

//// convert date/time from tzone, to time_point
std::chrono::system_clock::time_point datetime_to_timepoint(const DateTime &dt, const date::time_zone* tzone)
{
    using namespace std::chrono;
    using namespace date;

    auto ymd = year(dt.year)/dt.month/dt.day; // year_month_day type
    if (!ymd.ok()) { throw std::runtime_error("Invalid date"); }

    return zoned_time{tzone, local_days{ymd} + hours(dt.hour) + minutes(dt.min) + seconds(dt.sec)}.get_sys_time();
}

The above function can be called as follows

DateTime datetime{2016 /*year*/,  12 /*month*/,  24 /*day*/,     23 /* hour */,  0 /* min */,  0 /*sec*/}; // variable definition

auto tp = datetime_to_timepoint(datetime, date::current_zone()); // datetime from local timezone

using date::operator<<;
std::cout <<                                                      tp  << std::endl;
std::cout << date::zoned_time{"UTC",                              tp} << std::endl;
std::cout << date::zoned_time{date::current_zone(),               tp} << std::endl;
std::cout << date::zoned_time{date::locate_zone("Europe/Moscow"), tp} << std::endl;

// Will print e.g. (if you're in CET timezone...)
// 2016-12-24 22:00:00.000000000
// 2016-12-24 22:00:00.000000000 UTC
// 2016-12-24 23:00:00.000000000 CET
// 2016-12-25 01:00:00.000000000 MSK



auto tp2 = datetime_to_timepoint(datetime, date::locate_zone("America/New_York")); // datetime from New York

std::cout <<                                                      tp2  << std::endl;
std::cout << date::zoned_time{"UTC",                              tp2} << std::endl;
std::cout << date::zoned_time{date::current_zone(),               tp2} << std::endl;
std::cout << date::zoned_time{date::locate_zone("Europe/Moscow"), tp2} << std::endl;

// Will print
// 2016-12-25 04:00:00.000000000
// 2016-12-25 04:00:00.000000000 UTC
// 2016-12-25 05:00:00.000000000 CET //  e.g. (if you're in CET timezone...)
// 2016-12-25 07:00:00.000000000 MSK



datetime = DateTime{2018 /*year*/,  3 /*month*/,  25 /*day*/,     2 /* hour */,  10 /* min */,  0 /*sec*/};
auto tp3 = datetime_to_timepoint(datetime, date::locate_zone("Europe/Berlin"));

// Will throw, with the following message
// terminate called after throwing an instance of 'date::nonexistent_local_time'
//   what():  2018-03-25 02:10:00 is in a gap between
// 2018-03-25 02:00:00 CET and
// 2018-03-25 03:00:00 CEST which are both equivalent to
// 2018-03-25 01:00:00 UTC

struct tm holding the components

If you have a struct tm holding the components (as UTC), the following function sets a time_point:

// Ignores tm_isdst!
template <typename Clock, typename Duration>
void to_time_point(const std::tm& t,
                   std::chrono::time_point<Clock, Duration>& tp)
{
    using namespace std::chrono;
    using namespace date;
    int y = t.tm_year + 1900;
    auto ymd = year(y)/(t.tm_mon+1)/t.tm_mday; // Yields a year_month_day type
    if (!ymd.ok())
        throw std::runtime_error("Invalid date");
    tp =  sys_days(ymd) +
          hours(t.tm_hour) + minutes(t.tm_min) + seconds(t.tm_sec);
}

std::chrono::system_clock::time_point tp;
std::tm components = {...};
to_time_point(components, tp);

(by ecorm)

The first recipe below is based on an example from Howard Hinnant's CppCon 2015 slides on the date library.

using namespace date;
auto time = std::chrono::system_clock::now();
auto daypoint = floor<days>(time);
year_month_day ymd = daypoint;   // calendar date
hh_mm_ss tod{time - daypoint}; // Yields time_of_day type

// Obtain individual components as integers
auto y   = int(ymd.year());
auto m   = unsigned(ymd.month());
auto d   = unsigned(ymd.day());
auto h   = tod.hours().count();
auto min = tod.minutes().count();
auto s   = tod.seconds().count();

Alternatively, using struct tm to hold the components:

template <typename Clock, typename Duration>
std::tm to_calendar_time(std::chrono::time_point<Clock, Duration> tp)
{
    using namespace date;
    auto date = floor<days>(tp);
    auto ymd = year_month_day(date);
    auto weekday = year_month_weekday(date).weekday_indexed().weekday();
    hh_mm_ss tod{tp - date};
    days daysSinceJan1 = date - sys_days(ymd.year()/1/1);

    std::tm result{};
    result.tm_sec   = tod.seconds().count();
    result.tm_min   = tod.minutes().count();
    result.tm_hour  = tod.hours().count();
    result.tm_mday  = (ymd.day() - 0_d).count();
    result.tm_mon   = (ymd.month() - January).count();
    result.tm_year  = (ymd.year() - 1900_y).count();
    result.tm_wday  = (weekday - Sunday).count();
    result.tm_yday  = daysSinceJan1.count();
    result.tm_isdst = -1; // Information not available
    return result;
}

auto t = to_calendar_time(std::chrono::system_clock::now());
std::cout << t.tm_year + 1900 << "-" << t.tm_mon + 1 << "-" << t.tm_mday << " "
          << t.tm_hour << ":" << t.tm_min << ":" << t.tm_sec << "\n";

Normalizing y/m/d when it is !ok()

(by Howard Hinnant)

The following function will "normalize" a year_month_day, much like mktime normalizes a tm. This function is not part of the library, but is offered as an example if you find yourself needing things like this:

date::year_month_day
normalize(date::year_month_day ymd)
{
    using namespace date;
    ymd += months{0};
    ymd = sys_days{ymd};
    return ymd;
}

The first line simply adds 0 months to the ymd. If ymd.month() is 0, this will subtract one from the year and set the month to Dec. If ymd.month() is greater than 12, the year will be incremented as many times as appropriate, and the month will be brought within the proper range. This operation will do nothing if ymd.month().ok() is already true.

The second line will "normalize" the day field. The second line requires that the month field is already normalized. For example 2015_y/December/32 will become 2016_y/January/1. If the day field is already in range, there will be no change.

The conversion from year_month_day to sys_days calls this algorithm:

http://howardhinnant.github.io/date_algorithms.html#days_from_civil

And the conversion from sys_days back to year_month_day calls this algorithm:

http://howardhinnant.github.io/date_algorithms.html#civil_from_days

Example:

int
main()
{
    using namespace date;
    auto ymd = 2015_y/55/250;
    std::cout << ymd << (ymd.ok() ? "\n" : " invalid date\n");
    ymd += months{0};
    std::cout << ymd << (ymd.ok() ? "\n" : " invalid date\n");
    ymd = sys_days{ymd};
    std::cout << ymd << (ymd.ok() ? "\n" : " invalid date\n");
}

Outputs:

2015-55-250 invalid date
2019-07-250 invalid date
2020-03-06

When is it ok to be !ok()?

(by Howard Hinnant)

This library allows dates to silently fall into a state of !ok(). Why does not !ok() assert or throw? When is it ever ok to be !ok()?

Consider this problem:

I want to find all dates for some year y which are the 5th Friday of the month (because that is party day or whatever). Here is a very efficient function which collects all of the 5th Fridays of a year:

std::pair<std::array<date::year_month_day, 5>, unsigned>
fifth_friday(date::year y)
{
    using namespace date;
    constexpr auto nan = 0_y/0/0;
    std::array<year_month_day, 5> dates{nan, nan, nan, nan, nan};
    unsigned n = 0;
    for (auto m = January; true; ++m)
    {
        auto d = Friday[5]/m/y;
        if (d.ok())
        {
            dates[n] = year_month_day{d};
            ++n;
        }
        if (m == December)
            break;
    }
    return {dates, n};
}

It turns out that it is an invariant that every year will have either 4 or 5 months which will have 5 Fridays. So we can efficiently return the results as a pair<array<year_month_day, 5>, unsigned>, where the second member of the pair will always be either 4 or 5.

The first job is just to initialize the array with a bunch of year_month_days. I've arbitrarily chosen 0_y/0/0 as a good initialization value. What do I like about this value? One of the things I like is that it is !ok()!. If I accidentally access .first[4] when .second == 4, an extra bit of safety is that the resultant year_month_day is !ok(). So being able to construct these !ok() values without an assert or exception is important just for that reason (like a nan). The cost? Nothing. These are compile-time constants.

Next I iterate over each month for the year y. The first thing to do is construct the 5th Friday for this month/year pair:

auto d = Friday[5]/m/y;

Now since not every month has a 5th Friday, this may not result in a valid date. But in this function the proper response to constructing an invalid date is not an assert nor an exception. The proper response is to ignore the date and iterate on to the next month. If it is a valid date, then it pushed on to the result.

This function can be exercised like this:

int
main()
{
    using namespace std::chrono;
    using namespace date;
    auto current_year = year_month_day{floor<days>(system_clock::now())}.year();
    auto dates = fifth_friday(current_year);
    std::cout << "Fifth Friday dates for " << current_year << " are:\n";
    for (auto i = 0u; i < dates.second; ++i)
        std::cout << dates.first[i] << '\n';
}

The variable current_year is initialized with the current year in the UTC time zone (close enough for government work -- use "tz.h" if you need to make it more exact to your locale). Then it is a simple matter to feed current_year into fifth_friday and iterate over the results. This just output for me:

Fifth Friday dates for 2016 are:
2016-01-29
2016-04-29
2016-07-29
2016-09-30
2016-12-30

Next year it will output:

Fifth Friday dates for 2017 are:
2017-03-31
2017-06-30
2017-09-29
2017-12-29

Many invalid dates were computed during the execution of this program. And none of them represented errors.

How to find the next Monday (or Thursday)

(by Howard Hinnant)

Given a year_month_day, and a day of the week, how to I efficiently increment the year_month_day until it is the desired day of the week?

This is very easy. But first you have to decide: If the year_month_day is already the desired weekday, do you want to return the original year_month_day or add a week? There is no one right answer. We'll do it both ways here. First I'll show how to keep the original. It is then an easy modification to show how to jump to the next week.

date::year_month_day
next_weekday(date::year_month_day ymd, date::weekday target)
{
    using namespace date;
    sys_days sd = ymd;
    return sd + (target - weekday{sd});
}

The first thing to do is to convert the year_month_day to a sys_days. This is done because it is very efficient to find the day of the week of a sys_days (count of days), and to do day-oriented arithmetic on that data structure.

Next find out how many days we need to add to sd to get to the desired weekday. This is just the target weekday minus the current weekday. weekday subtraction is unsigned modulo 7. That is, it always returns a value between 0 and 6. Monday - Sunday is days{1} since Monday is always 1 day after Sunday. Sunday - Monday is days{6} since Sunday is always 6 days after Monday.

Above if target == weekday{sd}, then we don't add any days at all because ymd already has the desired weekday. Else we add up to 6 days. Then the return implicitly converts back to a year_month_day.

If we want to add a week when the input is already at the target weekday, then you just add a day to sd prior to the algorithm:

date::year_month_day
next_weekday(date::year_month_day ymd, date::weekday target)
{
    using namespace date;
    sys_days sd = ymd;
    sd += days{1};
    return sd + (target - weekday{sd});
}

The reverse is similar:

date::year_month_day
prev_weekday(date::year_month_day ymd, date::weekday target)
{
    using namespace date;
    sys_days sd = ymd;
    sd -= days{1};
    return sd - (weekday{sd} - target);
}

Except now we're subtracting weekdays to find out how many days to subtract from sd. Remove the pre-decrement by a day if you want this function to return the input when the input is already at the target weekday.

Converting from {year, microseconds} to CCSDS

(by Howard Hinnant)

CCSDS (http://public.ccsds.org/default.aspx) has a data structure that looks like this:

struct CCSDS
{
    std::uint16_t days;
    std::uint32_t ms;
    std::uint16_t us;
};

where days is the number of days since Jan 1, 1958, ms is the count of milliseconds of the current day, and us is the count of microseconds of the current millisecond.

A need arose to convert a {year, microsecond} data structure to the above CCSDS data structure, where the second component is the number of microseconds since the start of the year. Furthermore, the count of microseconds includes leap seconds.

Here is a function to perform that conversion:

CCSDS
to_CCSDS(date::year y, std::chrono::microseconds us)
{
    using namespace date;
    using namespace std::chrono;
    auto utc = clock_cast<utc_clock>(sys_days{y/January/1}) + us;
    auto sys = clock_cast<system_clock>(utc);
    auto dp = floor<days>(sys);
    auto d = dp - sys_days{1958_y/January/1};
    us = utc - clock_cast<utc_clock>(dp);
    auto ms = duration_cast<milliseconds>(us);
    us -= ms;
    return {static_cast<std::uint16_t>(d.count()),
            static_cast<std::uint32_t>(ms.count()),
            static_cast<std::uint16_t>(us.count())};
}

The variable utc holds the “year + us” as a time point with microseconds precision. This time point counts microseconds, including leap seconds, since 1970-01-01 00:00:00 UTC.

The next step is to find when the day started that is associated with utc. To do this one must convert utc back to “Unix time”, and then truncate that time point to a precision of days, resulting in the variable dp. dp is a count of days since 1970-01-01. Since the required epoch is 1958-01-01, this is taken into account in creating d, the first value needed in the return type.

Now the number of microseconds since the start of the day needs to be computed. The start of the day, dp, is converted back into the leap-second aware system, and subtracted from the microsecond time point: utc. The variable us is reused to hold “microseconds since midnight”. Now it is a simple computation to split this into milliseconds since midnight, and microseconds since the last millisecond.

Difference in months between two dates

(by Howard Hinnant)

I recently stumbled across this Stack Overflow question:

http://stackoverflow.com/q/19290421/576911

And I decided to see if I could answer it (here) using this library. The question asks: How can I get the number of months between two dates? And it gives two example dates:

auto d1 = 1_d/October/2013;
auto d2 = 30_d/October/2016;

(I've converted the syntax to that of this library).

The question isn't perfectly clear since "months" is not a very precise unit. Do we want the number of "full months"? Or perhaps we should round to the nearest number of integral months? Or do we want a floating-point representation of months which can show fractional months?

These are all reasonable possibilities, and you can compute all of these things with this library. Sometimes the hardest part of a question is sufficiently refining it until you know what is really being asked.

If we want to just ignore the day-field in these dates, and then compute the number of months, that is easily done like so:

std::cout << (d2.year()/d2.month() - d1.year()/d1.month()).count() << '\n';

This creates two year_month objects and subtracts them. This gives a std::chrono::duration that represents a signed-integral number of months. The output is:

36

To include the influence of the day-fields, it is best to convert d1 and d2 to sys_dayss:

auto dp1 = sys_days(d1);
auto dp2 = sys_days(d2);

Now we could (for example) subtract the two sys_dayss, and round the result to the nearest integral month:

std::cout << round<months>(dp2-dp1).count() << '\n';

This outputs:

37

Or we could create a new std::chrono::duration type based on float, but with a period of months, and convert the difference to that:

std::cout << duration<float, months::period>(dp2-dp1).count() << '\n';

This outputs:

36.9617

These are all reasonable answers to the question, and all easily computable with this library.

Working with the ISO week-based year

(by Howard Hinnant)

The ISO week date is an internationally recognized system of counting weeks of the year. This is in effect a separate calendar system, though it is closely related to the Gregorian calendar. Instead of specifying a year, month and day, each day is specified by year, week number, and day of the week. For example 2015-W51-Sat is a fully specified date. One can form such a date using the <iso_week.h> header like so:

using namespace iso_week::literals;
auto iso_date = 2015_y/51/sat;

Like <date.h>, you can specify an ISO week date in any of the three orders: y/wn/wd, wd/wn/y, wn/wd/y (big endian, little endian, mixed (american) endian).

Also like <date.h>, you can implicitly convert a ISO week date to sys_days, and vice-versa. For convenience, an alias of date:: sys_days exists as iso_week:: sys_days :

iso_week::sys_days dp = iso_date;

And recall that sys_days is just a type alias for a std::chrono::time_point<std::chrono::system_clock, days>. So the ISO week date (iso_week:year_weeknum_weekday) is immediately interoperable with the entire <chrono> library, just like date::year_month_day is.

auto now = std::chrono::system_clock::now();
auto dp = date::floor<iso_week::days>(now);
iso_week::year_weeknum_weekday iso_date = dp;
date::hh_mm_ss time{now-dp};
std::cout << iso_date << ' ' << time << '\n';

Which just output for me:

2016-W11-Sat 03:07:02.460737

And because iso_week:year_weeknum_weekday is implicitly convertible to and from sys_days, that makes it immediately (and explicitly) convertible to any other calendar system that is implicitly convertible to and from sys_days:

auto civil_date = date::year_month_day{iso_date};
std::cout << civil_date << ' ' << time << '\n';

which outputs:

2016-03-19 03:07:02.460737

And there you have it: sys_days is a Rosetta Stone for translating any calendar to any other calendar. Just make your calendar convert to and from sys_days, and you have interoperability with every other calendar which does so.

using namespace date::literals;
auto today = 2016_y/mar/19;
std::cout << "civil   : " << today << '\n';
std::cout << "Julian  : " << julian::year_month_day{today} << '\n';
std::cout << "Coptic  : " << coptic::year_month_day{today} << '\n';
std::cout << "iso_week: " << iso_week::year_weeknum_weekday{today} << '\n';

Output:

civil   : 2016-03-19
Julian  : 2016-03-06
Coptic  : 1732-07-10
iso_week: 2016-W11-Sat

This is somewhat of a teaser because as I write this the Julian and Coptic calendars aren't publicly available yet. The software exists, but is not fully tested yet. But more importantly, just follow the recipe in <iso_week.h> for your favorite calendar (convert to and from sys_days), and your calendar is now part of the club! Contribute your calendar so we can all use it!

2Gs Birthday

(by Howard Hinnant)

This example demonstrates both some simple date arithmetic, and how to handle discontinuities in a timezone. Dave was born in the "America/Los_Angeles" timezone at 10:03am on April 24, 1954. When will he be 2,000,000,000 seconds old in the same timezone?

#include <chrono>
#include <iostream>
#include "date/date.h"
#include "date/tz.h"

int
main()
{
    using namespace std::chrono_literals;
    using namespace date;
    //  Dave was born April 24, 1954. 10:03 AM pst
    //  Want to know when he is 2 Gigaseconds old
    zoned_time birthday{"America/Los_Angeles", local_days{April/24/1954} + 10h + 3min};
    std::cout << "born        : " << birthday << '\n';
    birthday = birthday.get_sys_time() + 2'000'000'000s;
    std::cout << "2Gs birthday: " << birthday << '\n';
}

One first creates the local time, and then pairs that to the time zone "America/Los_Angeles" using zoned_time. Then add 2Gs to the sys_time (not the local_time) of birthday. This outputs:

born        : 1954-04-24 10:03:00 PST
2Gs birthday: 2017-09-08 14:36:20 PDT

Note that without handling the timezone correctly, this result would be an hour off (2017-09-08 13:36:20) because the birth date falls in PST, and the celebration date falls in PDT.

Also note that this library correctly handles the changes in timezone rules throughout the decades. Several Apr 24ths have fallen within daylight saving over the decades, all of them since 1987.

But what about leap seconds?

This gets a little complicated.

Atomic time keeping started experimentally in 1955, about a year after Dave's birth. In 1958 the first atomic international time standard was begun: TAI. And then between 1958 and 1972 10 "undocumented" leap seconds were inserted to form what we now know as UTC. I say "undocumented" because they weren't inserted in units of a second, and I don't know how much was inserted when. I only know that the total was exactly 10s over this time period.

Assuming the birthdate is exactly synchronized with TAI (offset by the timezone), then we can form the birthday as tai_time:

auto zone = locate_zone("America/Los_Angeles");
auto birthday = to_tai_time(zoned_time{zone,
                local_days{April/24/1954} + 10h + 3min - 10s}.get_sys_time());

We have to subtract 10s manually because we want the birthday to be 1954-04-24 18:03:00 TAI and without that 10s subtraction we have UTC modeled back to 1954 instead of modeling TAI in 1954. Then we add the 2Gs in tai_time and convert that result back to sys_time, and then to a zoned_time:

zoned_time Day2Gs{zone, to_sys_time(birthday + 2'000'000'000s)};

Now the output is:

born        : 1954-04-24 18:03:00 TAI 
2Gs birthday: 2017-09-08 14:35:43 PDT

which is 37s earlier than we reported without taking leap seconds into account. Now ordinarily computing times to this accuracy (taking leap seconds into account) in the future would be a very dicey proposition as you never know when a leap second is going to be inserted in the future. However, as I write this (2016-10-24) the IANA database knows about the leap second to be inserted at 2017-01-01, and it is unlikely (though not impossible) that another leap second will be inserted prior to 2017-09-08. So I have a high confidence that the correct number of leap seconds has been taken into account. We should know for sure if this computation is exactly correct by 2017-02-01.

Calculating Ordinal Dates

(by Roland Bock)

An ordinal date consists of a year and a day of year (1st of January being day 1, 31st of December being day 365 or day 366). The year can be obtained directly from year_month_day. And calculating the day is wonderfully easy. In the code below we make us of the fact that year_month_day can deal with invalid dates like the 0th of January:

int main()
{
   using namespace date;
   
   const auto time = std::chrono::system_clock::now();
   const auto daypoint = floor<days>(time);
   const auto ymd = year_month_day{daypoint};
   
   // calculating the year and the day of the year
   const auto year = ymd.year();
   const auto year_day = daypoint - sys_days{year/January/0};
   
   std::cout << year << '-' << std::setfill('0') << std::setw(3) << year_day.count() << std::endl;
   
   // inverse calculation and check
   assert(ymd == year_month_day{sys_days{year/January/0} + year_day});
}

Local time arithmetic

(by Howard Hinnant)

It is generally accepted knowledge that doing time point arithmetic in "local time" is error prone because of UTC offset changes such as daylight saving time. For example when "springing forward" onto daylight saving time, the day is only 23 hours long instead of the normal 24.

However this library can be used to correctly do such computations very easily. Below is code that prints out 9am for several days in the "America/New_York", both just before the DST transition, and just after:

#include "date/tz.h"
#include <iostream>

int
main()
{
    using namespace std::chrono;
    using namespace date;
    zoned_time base{"America/New_York", local_days{March/11/2016} + 9h};
    for (int i = 0; i < 4; ++i)
    {
        std::cout << format("%F %T %z", base) << '\n';
        base = base.get_local_time() + days{1};
    }
}

This code outputs:

2016-03-11 09:00:00 -0500
2016-03-12 09:00:00 -0500
2016-03-13 09:00:00 -0400
2016-03-14 09:00:00 -0400

Note that the code simply gets the current local time, adds 1 day to it, and assigns that local time back into the zoned_time. As long as the newly computed local time is not ambiguous or non-existent, this code just works. And if the newly computed local time is ambiguous or non-existent, an exception will be thrown.

Convert a time zone abbreviation into a time zone

(by Howard Hinnant)

It is well known that converting a time zone abbreviation into a time zone is in general a difficult task. Not only do time zones keep changing what abbreviation they are using, but the abbreviations in use are not unique. At any given time multiple time zones could be using the exact same abbreviation. And not all of these time zones will always have the same UTC offset.

If nothing else, this operation can be extremely confusing and error prone. You should strive to not find yourself in the situation of needing to perform this operation. However, if you are forced into it, this library provides solutions that you can program yourself, which are superior to all other solutions.

To illustrate, this example develops find_by_abbrev functions which return a std::vector<date::zoned_time<some-duration>>. The client can then iterate over this list and inspect each zoned_time and output all information of interest to the client. Presumably some of this information can be used to choose a unique time zone.

To begin, let's say we have a date/time: 2016-04-03 03:15, and an abbreviation "CST". We want to find all possible time zones which might be using "CST" at 2016-04-03 03:15.

First problem: Is 2016-04-03 03:15 a time local to the time zone, or is it UTC? The answer to this question can make a difference in the results, so we need to be clear about what we're asking for. Both questions are valid: What time zones are using "CST" at this instant? What time zones have a local time_point t which uses "CST"?

So we need two find_by_abbrev: One which takes a sys_time, and one which takes a local_time. Let's do sys_time first:

#include "date/tz.h"
#include <string>
#include <iostream>
#include <vector>

template <class Duration>
std::vector<date::zoned_time<std::common_type_t<Duration, std::chrono::seconds>>>
find_by_abbrev(date::sys_time<Duration> tp, const std::string& abbrev)
{
    using namespace std::chrono;
    using namespace date;
    std::vector<zoned_time<std::common_type_t<Duration, std::chrono::seconds>>> results;
    auto& db = get_tzdb();
    for (auto& z : db.zones)
    {
        if (z.get_info(tp).abbrev == abbrev)
            results.emplace_back(&z, tp);
    }
    return results;
}

int
main()
{
    using namespace std::chrono;
    using namespace date;
    auto now = sys_days{2016_y/4/3} + 3h + 15min;
    auto v = find_by_abbrev(now, "CST");
    for (auto const& zt : v)
        std::cout << zt << " " << zt.get_time_zone()->name() << '\n';
}

The find_by_abbrev function is surprisingly simple: Loop over all time_zones in the database, get the sys_info for each one at time tp, and if that sys_info has an abbreviation equal to abbrev, create a zoned_time for this time_zone and tp and append it to the vector. The most complicated part is figuring out the precision of the duration of the zoned_time which is going be the finer of seconds and Duration. The facility std::common_type figures that out for us.

This program outputs:

2016-04-02 21:15:00 CST America/Bahia_Banderas
2016-04-02 21:15:00 CST America/Belize
2016-04-02 21:15:00 CST America/Costa_Rica
2016-04-02 21:15:00 CST America/El_Salvador
2016-04-02 21:15:00 CST America/Guatemala
2016-04-02 21:15:00 CST America/Managua
2016-04-02 21:15:00 CST America/Merida
2016-04-02 21:15:00 CST America/Mexico_City
2016-04-02 21:15:00 CST America/Monterrey
2016-04-02 21:15:00 CST America/Regina
2016-04-02 21:15:00 CST America/Swift_Current
2016-04-02 21:15:00 CST America/Tegucigalpa
2016-04-03 11:15:00 CST Asia/Macau
2016-04-03 11:15:00 CST Asia/Shanghai
2016-04-03 11:15:00 CST Asia/Taipei

These are all the times and locations using "CST" at 2016-04-03 03:15 UTC. Note that some of the places are on opposite sides of the planet.

Now the really interesting part of this problem is what happens if we are talking about 2016-04-03 03:15 local time? Around this time of year many regions are switching in/out of daylight saving time. And how do we compute that?

The driver program is identical to that shown above except that instead of:

auto now = sys_days{2016_y/4/3} + 3h + 15min;

we have:

auto now = local_days{2016_y/4/3} + 3h + 15min;

The find_by_abbrev takes a local_time<Duration> instead of a sys_time<Duration>, and the logic is slightly more complicated:

template <class Duration>
std::vector<date::zoned_time<std::common_type_t<Duration, std::chrono::seconds>>>
find_by_abbrev(date::local_time<Duration> tp, const std::string& abbrev)
{
    using namespace std::chrono;
    using namespace date;
    std::vector<zoned_time<std::common_type_t<Duration, std::chrono::seconds>>> results;
    auto& db = get_tzdb();
    for (auto& z : db.zones)
    {
        auto i = z.get_info(tp);
        switch (i.result)
        {
        case local_info::unique:
            if (i.first.abbrev == abbrev)
                results.emplace_back(&z, tp);
            break;
        case local_info::ambiguous:
            if (i.first.abbrev == abbrev)
                results.emplace_back(&z, tp, choose::earliest);
            else if (i.second.abbrev == abbrev)
                results.emplace_back(&z, tp, choose::latest);
            break;
        default:
            break;
        }
    }
    return results;
}

A local_time may or may not have a unique mapping to UTC. It might be unique, it might be ambiguous, or it might not exist at all. This function discovers if the mapping is unique, ambiguous or non-existent with i.result. If it is unique, the logic is exactly as with the find_by_abbrev for sys_time search. Note that zoned_time knows the difference between a sys_time and a local_time, and always does the right thing.

If the mapping is ambiguous, then there are two potential mappings from {local time, time zone abbrev} to UTC. If the first matches up with the abbreviation then the first is selected, else if the second matches up with the abbreviation then the second is selected. Else neither is selected.

If the mapping is non-existent, then it is ignored.

This program outputs:

2016-04-03 03:15:00 CST America/Belize
2016-04-03 03:15:00 CST America/Costa_Rica
2016-04-03 03:15:00 CST America/El_Salvador
2016-04-03 03:15:00 CST America/Guatemala
2016-04-03 03:15:00 CST America/Managua
2016-04-03 03:15:00 CST America/Regina
2016-04-03 03:15:00 CST America/Swift_Current
2016-04-03 03:15:00 CST America/Tegucigalpa
2016-04-03 03:15:00 CST Asia/Macau
2016-04-03 03:15:00 CST Asia/Shanghai
2016-04-03 03:15:00 CST Asia/Taipei

Note that all local times are now 03:15:00. Also note that this list is a subset of the previous list as by this time, some of the previous time zones have already switched to another abbreviation.

How you choose between these time zones is beyond the scope of this library. Perhaps you know a priori that you are dealing with time zones in Asia. Perhaps you know a priori that you are dealing with a time zone in the US. By whatever application-specific logic you have, these functions give you the tools to narrow your search to the possible time zones.

Find all instances when a daylight savings shift is not 1 hour

(by Howard Hinnant)

Let's say you want to search the globe, and all time, for time zones when the daylight savings shift was not 1 hour. Sound strange? Maybe, but this code teaches you how to efficiently iterate over all timezone transitions and inspect their characteristics. So you can use this code for all kinds of searches over time zones.

#include "date/tz.h"
#include <iostream>

int
main()
{
    using namespace date;
    using namespace std::chrono_literals;
    auto& db = get_tzdb();
    for (auto const& z : db.zones)
    {
        auto begin = sys_days{January/1/year::min()} + 0s;
        auto end   = sys_days{January/1/2035} + 0s;
        do
        {
            auto info = z.get_info(begin);
            if (info.save != 0h && info.save != 1h)
            {
                std::cout << z.name() << " has a daylight savings offset of "
                          << info.save.count() << "min from " << info.begin
                          << " UTC to " << info.end << " UTC with the abbreviation "
                          << info.abbrev << '\n';
            }
            begin = info.end;
        } while (begin < end);
    }
}

You first get a reference to the tz database, then iterate over each zone in the database. For each zone, set a range of time points to search over. In this example I start searching as far back as possible, and search forward to the year 2035.

Starting at the beginning of time, get an sys_info for that UTC time_point. An sys_info looks like this:

struct sys_info
{
    second_point         begin;
    second_point         end;
    std::chrono::seconds offset;
    std::chrono::minutes save;
    std::string          abbrev;
};

Each time zone transition happens at begin (UTC). The total offset from UTC for this timezone and period is offset. This offset will be in effect until end (UTC). The difference between this period's "normal" offset, and this offset is save. And this period's timezone abbreviation is abbrev.

For this example, we are looking for those periods when the save is neither 0 minutes, nor 60 minutes. When we find one, just print it out.

To increment the loop, set the local variable begin to info.end, and look up a new Info.

It is really remarkably simple to search the globe and and all time for interesting chronological events related to timezone transitions.

Sample output of this program:

Africa/Accra has a daylight savings offset of 20min from 1920-09-01 00:00:00 UTC to 1920-12-30 23:40:00 UTC with the abbreviation GHST
...

How many timezones are using daylight saving?

(by Howard Hinnant)

Ever wonder how the global use of daylight saving time is trending with time? Here's one way to find out:

#include "date/tz.h"
#include <iostream>

int
main()
{
    using namespace date;
    using namespace std::chrono_literals;
    auto& db = get_tzdb();
    std::cout << db.version << '\n';
    for (auto y = 1850_y; y < 2020_y; ++y)
    {
        auto use_daylight = 0;
        auto total = 0;
        for (auto& z : db.zones)
        {
            ++total;
            auto info1 = z.get_info(sys_days{y/January/15});
            auto info2 = z.get_info(sys_days{y/July/15});
            if (info1.save != 0min || info2.save != 0min)
                ++use_daylight;
        }
        std::cout << y << " : " << use_daylight << '/' << total
                  << " = " << static_cast<float>(use_daylight)/total << '\n';
    }
}

This code loops over a wide range of years, and then for each year, loops over all timezones in the database, and for each timezone, detects whether it is switching back and forth between standard time and daylight time for that year. The switch detection is rather crude, but you can make this detection as elaborate as you want. Currently it picks two dates 6 months apart which are unlikely to both be using standard time if the zone is using daylight saving time that year. For each of those two dates, the sys_info.save member is checked. If either save != 0min, daylight saving is in use that year.

The results of this program are plotted below (the plotting code is not part of the above program).

Plot of daylight saving use by year

What is the epoch difference between Unix Time and GPS time?

(by Howard Hinnant)

This question is interesting because it has more than one answer. And the reason it has more than one answer is because there are more than one measures. You can blame it all on leap seconds. But hopefully this library can help clarify the situation for you.

Unix Time is what sys_time measures. This is a count of non-leap-seconds since 1970-01-01. This measure is helpful because it provides an efficient vehicle for converting between field types such as {year, month, day, hours, minutes, seconds} and {count_of_seconds}. It is what is measured by std::chrono::system_clock, gettimeofday, std::time, etc. Unfortunately this solution is not without its problems. One of them is that there exists no Unix Time which represents inserted leaps seconds in utc_time (and subsequently, even some seconds in tai_time and gps_time).

This all means that subtracting two sys_time time points can give a different result than subtracting two time points of type utc_time, tai_time, or gps_time, and this example highlights that difference.

First of all we need to get the gps_time epoch and the sys_time epoch. We do not even need to know the dates of these epochs. We can just use 0:

auto gps_epoch  = gps_seconds{0s};  // 1980-01-06 00:00:00 UTC
auto unix_epoch = sys_seconds{0s};  // 1970-01-01 00:00:00 UTC

These are both std::chrono::time_points, but if we try to subtract them we will get a compile-time error because they refer to different clocks. So to subtract them we must convert one to the other prior to subtracting. Here is one way to do this:

std::cout << (gps_epoch - to_gps_time(unix_epoch)).count() << "s\n";

This converts the sys_time to a gps_time, does the subtraction, and outputs:

315964809s

In gps_time, there are 315,964,809 seconds between these two epochs.

Here is another way:

std::cout << (to_sys_time(gps_epoch) - unix_epoch).count() << "s\n";

which outputs:

315964800s

In sys_time, there are 315,964,800 seconds between these two epochs.

Why the 9s difference? Because there were 9 leap seconds inserted between these two epochs, and sys_time does not count those 9 seconds, gps_time does.

If you were to do this same experiment but using utc_time and gps_time, both measures would result in 315964809s because both take leap seconds into account.

Now wait a second, doesn't gps_time ignore leap seconds?! Yes, but it does so by rolling its conversion to the civil calendar forward by one second. Thus it doesn't ignore the physical second. It ignores the time of day. sys_time on the other hand, ignores the physical second's existence in the first place, thus keeping the mapping to the civil calendar the same as UTC.

You can discover all kinds of neat subtleties by playing with sys_time, utc_time, tai_time, and gps_time.

How to convert to/from C++ Builder's TDate and TDateTime

(by Howard Hinnant)

If you are using TDateTime from C++ Builder you may occasionally need functionality that is not present in that library, but is in this one. This article explains how to convert back and forth between these two libraries, so that you can easily use the functionality of both.

Two important facts:

  1. The epoch for TDateTime is 1899-12-30 00:00:00 UTC.
  2. The epoch for std::chrono::system_clock is (unspecified but de facto): 1970-01-01 00:00:00 UTC.

TDateTime stores a double. The integral part of that double counts days since the epoch. The fractional part stores fractions of a day, but in a strange encoding for times prior to its epoch:

When working with negative System::TDateTime values, computations must handle time portion separately. The fractional part reflects the fraction of a 24-hour day without regard to the sign of the System::TDateTime value. For example, 6:00 am on 12/29/1899 is –1.25, not –1 + 0.25, which would be –0.75. There are no System::TDateTime values between –1 and 0.

We'll provide two bidirectional conversions:

TDate     <---> sys_days
TDateTime <---> sys_time<D>

The TDate/sys_days conversions don't have to worry about the fractional day issue for negative TDateTime values, and so they are both easier and more efficient:

date::sys_days
to_sys_days(System::TDate td)
{
    using namespace date;
    return sys_days(days{static_cast<int>(td)} -
        (sys_days{1970_y/January/1} - sys_days{1899_y/December/30}));
}

The only thing to do here is to extract the integral value from the TDate, convert that into sys_days and subtract the difference between the two epochs. The reverse conversion is just as easy:

System::TDate
to_TDate(date::sys_days sd)
{
    using namespace date;
    return System::TDate(static_cast<int>((sd.time_since_epoch() +
        (sys_days{1970_y/January/1} - sys_days{1899_y/December/30})).count()));
}

These can be exercised like this:

int
main()
{
    using namespace date;
    using namespace System;
    std::cout << to_sys_days(TDate{0}) << '\n';
    std::cout << to_sys_days(TDate{2}) << '\n';
    std::cout << to_sys_days(TDate{-1}) << '\n';
    std::cout << to_sys_days(TDate{35065}) << '\n';

    std::cout << (int)to_TDate(1899_y/December/30) << '\n';
    std::cout << (int)to_TDate(1900_y/January/1) << '\n';
    std::cout << (int)to_TDate(1899_y/December/29) << '\n';
    std::cout << (int)to_TDate(1996_y/January/1) << '\n';
}

which outputs:

1899-12-30
1900-01-01
1899-12-29
1996-01-01
0
2
-1
35065

This output is consistent with what is in the TDateTime documentation.

For converting to/from TDateTime it is convenient to allow the client to choose the precision of the sys_time to convert to or from. For example this converts to a precision of minutes:

to_sys_time<minutes>(TDateTime{2.75})

while this converts to a precision of milliseconds:

to_sys_time<milliseconds>(TDateTime{2.75})

Here's the implementation:

template <class D>
date::sys_time<D>
to_sys_time(System::TDateTime dt)
{
    using namespace date;
    using namespace std::chrono;
    using fdays = duration<double, days::period>;
    using ftime = time_point<system_clock, fdays>;
    auto ft = ftime{fdays{static_cast<double>(dt)}};
    if (ft < ftime{})
    {
        auto d = time_point_cast<days>(ft);
        auto t = d - ft;
        ft = d + t;
    }
    ft -= sys_days{1970_y/January/1} - sys_days{1899_y/December/30};
    return round<D>(ft);
}

For time points not prior to the TDateTime epoch, it is quite straightforward. It helps to create a duration and chrono time_point based on double that counts days. Then one simply extracts the double from the TDateTime and converts it to our double-based time_point, subtracts the difference in the epochs, and then uses the round<D> facility to truncate the result to the requested precision.

If this is a pre-epoch TDateTime, then there's an extra dance to treat the integral and fractional parts of the double separately.

The reverse conversion is similar:

template <class D>
System::TDateTime
to_TDateTime(date::sys_time<D> tp)
{
    using namespace date;
    using namespace std::chrono;
    using fdays = duration<double, days::period>;
    using ftime = time_point<system_clock, fdays>;
    auto ft = ftime{tp} + (sys_days{1970_y/January/1} - sys_days{1899_y/December/30});
    if (ft >= ftime{})
        return System::TDateTime(ft.time_since_epoch().count());
    auto d = floor<days>(ft);
    auto t = d - ft;
    return System::TDateTime((d + t).time_since_epoch().count());
}

This can all be exercised like this:

int
main()
{
    using namespace date;
    using namespace std::chrono;
    using namespace System;
    std::cout << to_sys_time<minutes>(TDateTime{0.}) << '\n';
    std::cout << to_sys_time<minutes>(TDateTime{2.75}) << '\n';
    std::cout << to_sys_time<minutes>(TDateTime{-1.25}) << '\n';
    std::cout << to_sys_time<minutes>(TDateTime{35065.}) << '\n';

    std::cout << (double)to_TDateTime(sys_days{1899_y/December/30} + 0min) << '\n';
    std::cout << (double)to_TDateTime(sys_days{1900_y/January/1} + 18h + 0min) << '\n';
    std::cout << (double)to_TDateTime(sys_days{1899_y/December/29} + 6h + 0min) << '\n';
    std::cout << (double)to_TDateTime(sys_days{1996_y/January/1} + 0min) << '\n';
}

which outputs:

1899-12-30 00:00
1900-01-01 18:00
1899-12-29 06:00
1996-01-01 00:00
0
2.75
-1.25
35065

How to convert to/from QDate

(by Howard Hinnant)

Here are functions you can use to convert between QDate and sys_days:

date::sys_days
to_sys_days(QDate qd)
{
    using namespace date;
    return sys_days{days{qd.toJulianDay()} -
        (sys_days{1970_y/January/1} - sys_days{year{-4713}/November/24})};
}

QDate
to_QDate(date::sys_days sd)
{
    using namespace date;
    return QDate::fromJulianDay((sd.time_since_epoch() +
        (sys_days{1970_y/January/1} - sys_days{year{-4713}/November/24})).count());
}

These work by simply adjusting the epoch of these two types.

How to convert between Windows' FILETIME and system_clock

(by Billy O'Neal)

Here are functions and typedefs you can use to work with Windows' FILETIME structure.

Assuming system_clock is based on the Unix epoch:

using std::ratio;
using std::chrono::duration;
using std::chrono::duration_cast;
using std::chrono::system_clock;

// filetime_duration has the same layout as FILETIME; 100ns intervals
using filetime_duration = duration<int64_t, ratio<1, 10'000'000>>;
// January 1, 1601 (NT epoch) - January 1, 1970 (Unix epoch):
constexpr duration<int64_t> nt_to_unix_epoch{INT64_C(-11644473600)};

system_clock::time_point FILETIME_to_system_clock(FILETIME fileTime) {
    const filetime_duration asDuration{static_cast<int64_t>(
        (static_cast<uint64_t>(fileTime.dwHighDateTime) << 32)
            | fileTime.dwLowDateTime)};
    const auto withUnixEpoch = asDuration + nt_to_unix_epoch;
    return system_clock::time_point{
        duration_cast<system_clock::duration>(withUnixEpoch)};
}

FILETIME system_clock_to_FILETIME(system_clock::time_point systemPoint) {
    const auto asDuration = duration_cast<filetime_duration>(
        systemPoint.time_since_epoch());
    const auto withNtEpoch = asDuration - nt_to_unix_epoch;
    const uint64_t rawCount = withNtEpoch.count();
    FILETIME result;
    result.dwLowDateTime = static_cast<DWORD>(rawCount); // discards upper bits
    result.dwHighDateTime = static_cast<DWORD>(rawCount >> 32);
    return result;
}

How to convert to/from boost::posix_time::ptime

(by Howard Hinnant)

boost::posix_time::ptime and std::chrono::system_clock::time_point represent the same thing in their respective libraries (boost and chrono). So it is nice to be able to convert between them. This is how to do it:

#include "boost/date_time/posix_time/posix_time.hpp"
#include <chrono>

std::chrono::system_clock::time_point
to_system_clock(boost::posix_time::ptime const& pt)
{
    using namespace std;
    using namespace boost;
    auto const dt = pt - posix_time::from_time_t(0);
#ifdef BOOST_DATE_TIME_HAS_NANOSECONDS
    chrono::nanoseconds ns{dt.total_nanoseconds()};
    auto sysd = chrono::duration_cast<chrono::system_clock::duration>(ns);
    return chrono::system_clock::time_point{sysd};
#else
    chrono::microseconds us{dt.total_microseconds()};
    return chrono::system_clock::time_point{us};
#endif
}

boost::posix_time::ptime
to_ptime(std::chrono::system_clock::time_point const& st)
{
    using namespace std;
    using namespace boost;
#ifdef BOOST_DATE_TIME_HAS_NANOSECONDS
    posix_time::nanoseconds fs{
        chrono::nanoseconds{st.time_since_epoch()}.count()};
#else
    posix_time::microseconds fs{
        chrono::duration_cast<chrono::microseconds>(
            st.time_since_epoch()).count()};
#endif
    return posix_time::from_time_t(0) + fs;
}

Print out a compact calendar for the year

(by Howard Hinnant)

Printing out the calendar for an entire year is an interesting exercise. You can either just take code and use it (a neat and useful utility), or you can study its implementation and learn much more about "date.h".

First the code, and then a detailed explanation:

#include "date/date.h"
#include <algorithm>
#include <iomanip>
#include <iostream>
#include <locale>
#include <ostream>
#include <stdexcept>
#include <string>

date::year
current_year()
{
    using namespace std::chrono;
    using namespace date;
    year_month_day ymd = floor<days>(system_clock::now());
    return ymd.year();
}

// The number of weeks in a calendar month layout plus 2 more for the calendar titles
unsigned
number_of_lines_calendar(date::year_month const ym, date::weekday const firstdow)
{
    using namespace date;
    return static_cast<unsigned>(
        ceil<weeks>((weekday{ym/1} - firstdow) + ((ym/last).day() - day{0})).count()) + 2;
}

// Print one line of a calendar month
void
print_line_of_calendar_month(std::ostream& os, date::year_month const ym,
                             unsigned const line, date::weekday const firstdow)
{
    using namespace std;
    using namespace date;
    switch (line)
    {
    case 0:
        // Output month and year title
        os << left << setw(21) << format(os.getloc(), " %B %Y", ym) << right;
        break;
    case 1:
        {
        // Output weekday names title
        auto wd = firstdow;
        do
        {
            auto d = format(os.getloc(), "%a", wd);
            d.resize(2);
            os << ' ' << d;
        } while (++wd != firstdow);
        break;
        }
    case 2:
        {
        // Output first week prefixed with spaces if necessary
        auto wd = weekday{ym/1};
        os << string(static_cast<unsigned>((wd-firstdow).count())*3, ' ');
        auto d = 1_d;
        do
        {
            os << format(" %e", d);
            ++d;
        } while (++wd != firstdow);
        break;
        }
    default:
        {
        // Output a non-first week:
        // First find first day of week
        unsigned index = line - 2;
        auto sd = sys_days{ym/1};
        if (weekday{sd} == firstdow)
            ++index;
        auto ymdw = ym/firstdow[index];
        if (ymdw.ok()) // If this is a valid week, print it out
        {
            auto d = year_month_day{ymdw}.day();
            auto const e = (ym/last).day();
            auto wd = firstdow;
            do
            {
                os << format(" %e", d);
            } while (++wd != firstdow && ++d <= e);
            // Append row with spaces if the week did not complete
            os << string(static_cast<unsigned>((firstdow-wd).count())*3, ' ');
        }
        else  // Otherwise not a valid week, output a blank row
            os << string(21, ' ');
        break;
        }
    }
}

void
print_calendar_year(std::ostream& os, unsigned const cols = 3,
                    date::year const y = current_year(),
                    date::weekday const firstdow = date::Sunday)
{
    using namespace date;
    if (cols == 0 || 12 % cols != 0)
        throw std::runtime_error("The number of columns " + std::to_string(cols)
                                 + " must be one of [1, 2, 3, 4, 6, 12]");
    // Compute number of lines needed for each calendar month
    unsigned ml[12] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
    for (auto& m : ml)
        m = number_of_lines_calendar(y/month{m}, firstdow);
    for (auto r = 0u; r < 12/cols; ++r) // for each row
    {
        const auto lines = *std::max_element(std::begin(ml) + (r*cols),
                                             std::begin(ml) + ((r+1)*cols));
        for (auto l = 0u; l < lines; ++l) // for each line
        {
            for (auto c = 0u; c < cols; ++c) // for each column
            {
                if (c != 0)
                    os << "   ";
                print_line_of_calendar_month(os, y/month{r*cols + c+1}, l, firstdow);
            }
            os << '\n';
        }
        os << '\n';
    }
}

int
main()
{
    print_calendar_year(std::cout);
}

As written this program outputs:

 January 2016            February 2016           March 2016          
 Su Mo Tu We Th Fr Sa    Su Mo Tu We Th Fr Sa    Su Mo Tu We Th Fr Sa
                 1  2        1  2  3  4  5  6           1  2  3  4  5
  3  4  5  6  7  8  9     7  8  9 10 11 12 13     6  7  8  9 10 11 12
 10 11 12 13 14 15 16    14 15 16 17 18 19 20    13 14 15 16 17 18 19
 17 18 19 20 21 22 23    21 22 23 24 25 26 27    20 21 22 23 24 25 26
 24 25 26 27 28 29 30    28 29                   27 28 29 30 31      
 31                                                                  

 April 2016              May 2016                June 2016           
 Su Mo Tu We Th Fr Sa    Su Mo Tu We Th Fr Sa    Su Mo Tu We Th Fr Sa
                 1  2     1  2  3  4  5  6  7              1  2  3  4
  3  4  5  6  7  8  9     8  9 10 11 12 13 14     5  6  7  8  9 10 11
 10 11 12 13 14 15 16    15 16 17 18 19 20 21    12 13 14 15 16 17 18
 17 18 19 20 21 22 23    22 23 24 25 26 27 28    19 20 21 22 23 24 25
 24 25 26 27 28 29 30    29 30 31                26 27 28 29 30      

 July 2016               August 2016             September 2016      
 Su Mo Tu We Th Fr Sa    Su Mo Tu We Th Fr Sa    Su Mo Tu We Th Fr Sa
                 1  2        1  2  3  4  5  6                 1  2  3
  3  4  5  6  7  8  9     7  8  9 10 11 12 13     4  5  6  7  8  9 10
 10 11 12 13 14 15 16    14 15 16 17 18 19 20    11 12 13 14 15 16 17
 17 18 19 20 21 22 23    21 22 23 24 25 26 27    18 19 20 21 22 23 24
 24 25 26 27 28 29 30    28 29 30 31             25 26 27 28 29 30   
 31                                                                  

 October 2016            November 2016           December 2016       
 Su Mo Tu We Th Fr Sa    Su Mo Tu We Th Fr Sa    Su Mo Tu We Th Fr Sa
                    1           1  2  3  4  5                 1  2  3
  2  3  4  5  6  7  8     6  7  8  9 10 11 12     4  5  6  7  8  9 10
  9 10 11 12 13 14 15    13 14 15 16 17 18 19    11 12 13 14 15 16 17
 16 17 18 19 20 21 22    20 21 22 23 24 25 26    18 19 20 21 22 23 24
 23 24 25 26 27 28 29    27 28 29 30             25 26 27 28 29 30 31
 30 31                                                               

But the above program is very flexible and can localize this output into a wide variety of formats to accommodate your preferences.

date::year
current_year();

All current_year() does is find the current year. The UTC time zone is used for simplicity. If this is not sufficient for your needs, it is easy enough to specify which year you want a calendar for. This works by calling system_clock::now(), truncating that result into sys_days (a count of days), converting the sys_days into a year_month_day, and returning the year() field of that.

unsigned
number_of_lines_calendar(date::year_month const ym, date::weekday const firstdow);

This function computes the number of lines that printing out the calendar will take for the year/month combination ym and using firstdow as the first day of the week for that calendar. The first thing to compute is the number of days the first of the month is past the first day of the week: (weekday{ym/1} - firstdow). Week day subtraction is unsigned modulo 7, so this is always a positive number in the range [0, 6], no matter the underlying encoding of weekday.

Add to that the number of days in the month. This is computed by ((ym/last).day() - day{0}). The expression ym/last creates a year_month_day_last which represents the date of the last day of the month. .day() extracts the day field. We could have subtracted day{1}, and then added days{1} to that difference, but it is simpler to just subtract the invalid day{0}. The type of this difference is days, a chrono::duration.

Next we want to convert this number of days into weeks using ceil which will round up if the conversion is not exact. This allows for months that don't completely fill out their last row. We then extract the number of weeks with .count() and add 2 more lines: One for the day-of-the-week title, and one for the month year title.

void
print_line_of_calendar_month(std::ostream& os, date::year_month const ym,
                             unsigned const line, date::weekday const firstdow);

This is the heart of the calendar-printing logic. This prints one line of a month calendar, with no line break afterwards. The argument line says which line [0, infinity]. If more lines are asked for than the calendar takes up, blank lines are printed. The calendar starts with the weekday firstdow. The entire function is just a switch on line to see which line to print out:

0: Print out the month and year title using ym and the locale extracted from os. Left align this output in a field of 21 spaces, except indent by one space.

1: Print out the day-of-the-week header using the first two letters of the localized abbreviation for the days of the week. Start with first dow and repeat for 1 week. Each weekday name is two letters long and right justified in a width of 3 spaces.

2: Print out the first week. This is the only week that may be prefixed with spaces. The number of spaces to prefix with is 3 for every day that the first of the month is past the first day of the week: weekday{ym/1} - firstdow. Then starting with 1_d, print each day (not 0-prefixed) right-justified in a field of 3 spaces. Iterate until incrementing the day of the week comes back around to firstdow.

3 - infinity: This can print either a week, beginning with ym/firstdow[index], or a blank line. It will print the latter if ym/firstdow[index] is not a valid date. The expression ym/firstdow[index] means the nthfirstdow of the month for this year. The index is a function of the line count, and whether or not we output a firstdow for line == 2. If we output a firstdow for line 2, then index == line - 1, other index == line - 2. For example when line == 3, most of the time we are looking for the first firstdow for this year/month.

Once we have ymdw (the first date to print out for this line), we need to convert that to a year_month_day so we can extract the day field from that. And we also need to compute the last day of the month (stored in e). Then we iterate from firstdow, printing out the day field (right-justified in a field of 3 spaces) until either we've printed a full week, or until we've printed the last day of the month, whichever comes first.

Finally we check if we output a full week, and if not, how many days for the week did we not print out (firstdow - wd). If this is non-zero, then we pad the line with 3 spaces for each day.

And that concludes the hardest part of the hardest function for this entire utility!

void
print_calendar_year(std::ostream& os, unsigned const cols = 3,
                    date::year const y = current_year(),
                    date::weekday const firstdow = date::Sunday);

This function prints the yearly calendar to os by calling the functions we've already defined. The calendar is in a format of cols by rows months, where cols is input by the client and represents how many months you want to print out horizontally. This argument must be one of [1, 2, 3, 4, 6, 12]. The example output above defaulted this to 3 months across by 4 down. The year can be input, or defaults to the current year UTC. And the day of the week which the calendar starts with can be specified and defaults to Sunday.

The first thing to do is check that cols has a proper value and throw an exception if it doesn't.

Next we need to find out how many lines each month of the year y needs. This is done by calling number_of_lines_calendar for each month of this year and storing the result in ml.

Then we have 3 nested loops. The outer loop runs over the number of rows (the number of calendar months vertically) which is 12/cols. Then for each row, compute the maximum number of lines necessary to output each month for this row. Then for each line, loop over the number of columns (monthly calendars to output horizontally).

Now just print out one line for each calendar for this {row, col, line} combination. The month for this combination is the one numbered r*cols + c + 1 (one-based row-major indexing). Also print out 3 spaces between each month horizontally and one blank line between each row of calendars for nice padding.

This program can now be used to localize and output a wide variety of calendars. For example is a German calendar in a 4x3 output for the year 2016 (output on macOS which supports this localization):

using namespace date::literals;
std::cout.imbue(std::locale("de_DE"));
print_calendar_year(std::cout, 4, 2016_y, Monday);

which outputs:

 Januar 2016             Februar 2016            März 2016              April 2016          
 Mo Di Mi Do Fr Sa So    Mo Di Mi Do Fr Sa So    Mo Di Mi Do Fr Sa So    Mo Di Mi Do Fr Sa So
              1  2  3     1  2  3  4  5  6  7        1  2  3  4  5  6                 1  2  3
  4  5  6  7  8  9 10     8  9 10 11 12 13 14     7  8  9 10 11 12 13     4  5  6  7  8  9 10
 11 12 13 14 15 16 17    15 16 17 18 19 20 21    14 15 16 17 18 19 20    11 12 13 14 15 16 17
 18 19 20 21 22 23 24    22 23 24 25 26 27 28    21 22 23 24 25 26 27    18 19 20 21 22 23 24
 25 26 27 28 29 30 31    29                      28 29 30 31             25 26 27 28 29 30   

 Mai 2016                Juni 2016               Juli 2016               August 2016         
 Mo Di Mi Do Fr Sa So    Mo Di Mi Do Fr Sa So    Mo Di Mi Do Fr Sa So    Mo Di Mi Do Fr Sa So
                    1           1  2  3  4  5                 1  2  3     1  2  3  4  5  6  7
  2  3  4  5  6  7  8     6  7  8  9 10 11 12     4  5  6  7  8  9 10     8  9 10 11 12 13 14
  9 10 11 12 13 14 15    13 14 15 16 17 18 19    11 12 13 14 15 16 17    15 16 17 18 19 20 21
 16 17 18 19 20 21 22    20 21 22 23 24 25 26    18 19 20 21 22 23 24    22 23 24 25 26 27 28
 23 24 25 26 27 28 29    27 28 29 30             25 26 27 28 29 30 31    29 30 31            
 30 31                                                                                       

 September 2016          Oktober 2016            November 2016           Dezember 2016       
 Mo Di Mi Do Fr Sa So    Mo Di Mi Do Fr Sa So    Mo Di Mi Do Fr Sa So    Mo Di Mi Do Fr Sa So
           1  2  3  4                    1  2        1  2  3  4  5  6              1  2  3  4
  5  6  7  8  9 10 11     3  4  5  6  7  8  9     7  8  9 10 11 12 13     5  6  7  8  9 10 11
 12 13 14 15 16 17 18    10 11 12 13 14 15 16    14 15 16 17 18 19 20    12 13 14 15 16 17 18
 19 20 21 22 23 24 25    17 18 19 20 21 22 23    21 22 23 24 25 26 27    19 20 21 22 23 24 25
 26 27 28 29 30          24 25 26 27 28 29 30    28 29 30                26 27 28 29 30 31   
                         31                                                                  

Parsing unambiguous date time inside daylight transition

(by Tai Meng)

We needed the ability to (de)serialize date time values. We investigated how we could parse an input string that looks like this:

1) 1999-10-31 01:30:00 US/Pacific PST

After consulting Howard, we learned that the input string should instead look like this:

2) 1999-10-31 01:30:00 -08:00 US/Pacific

and then the solution is simple. To convert PST to -08:00 is in general non-trivial and often requires user input, because abbreviations often match multiple timezones. This recipe assumes that users of this recipe will have a way to re-format strings of form 1) into strings of form 2).

Once we have a string of form 2), give this sample code a try (thank you Aaron who provided the draft):

#include <iostream>
#include <sstream>
#include <string>
#include "date/tz.h"

int main()
{
    using namespace std;
    using namespace date;

    istringstream inputStream{ "1999-10-31 01:30:00 -08:00 US/Pacific" };

    // Using local_seconds would resolve in ambiguous date exception
    sys_seconds tp;
    string tz_name;
    inputStream >> parse("%F %T %Ez %Z", tp, tz_name);

    // bool operator tells us whether stream was successfully parsed
    assert(bool(inputStream));

    zoned_time zt{tz_name, tp};

    // This will output America/Los_Angeles, because US/Pacific is an alias of it.
    cout << format("%F %T %Ez", zt) << ' ' << zt.get_time_zone()->name() << '\n';
}

Update from Howard:

There is now functionality to parse the original format:

1) 1999-10-31 01:30:00 US/Pacific PST

And check if the time zone abbreviation is consistent, and in the ambiguous case, use the time zone abbreviation to disambiguate the time stamp:

#include <iostream>
#include <sstream>
#include <string>
#include "date/tz.h"

int main()
{
    using namespace std;
    using namespace date;

    istringstream inputStream{ "1999-10-31 00:30:00 US/Pacific PST" };

    // Using local_seconds would resolve in ambiguous date exception
    local_seconds tp;
    string tz_name;
    string tz_abbrev;
    inputStream >> parse("%F %T", tp) >> tz_name >> tz_abbrev;

    // bool operator tells us whether stream was successfully parsed
    assert(bool(inputStream));

    // Check for ambiguous and nonexistent timestamps
    auto zone = locate_zone(tz_name);
    auto info = zone->get_info(tp);
    zoned_seconds zt{zone};
    switch (info.result)
    {
    case local_info::unique:
        zt = tp;  // easy case
        // One can check that the tz_abbrev is consistent with
        //   info.first.abbrev if desired.
        break;
    case local_info::nonexistent:
        // time stamp never existed.  Throw an error?
        // Or here is how map to a unique UTC equivalent:
        zt = zoned_time{zone, tp, choose::earliest};  // choose::latest also
                                                      //   gives same answer.
        break;
    case local_info::ambiguous:
        // Use tz_abbrev to break the ambiguity
        if (info.first.abbrev == tz_abbrev)
            zt = zoned_time{zone, tp, choose::earliest};
        else if (info.second.abbrev == tz_abbrev)
            zt = zoned_time{zone, tp, choose::latest};
        else
            throw std::runtime_error(tz_abbrev +
                " is not a valid abbreviation for " + tz_name);
        break;
    }

    // This will output America/Los_Angeles, because US/Pacific is an alias of it.
    cout << format("%F %T %Ez", zt) << ' ' << zt.get_time_zone()->name() << '\n';
}

This involves getting the local_info structure from the time_zone for that local_time. The local_info will have all of the information about that time_zone/local_time combination, including whether there is a unique mapping to UTC, a non-existing mapping (as in the gap created by "spring forward"), or an ambiguous mapping (created by a local time occurring twice during a "fall back").

In the ambiguous case you can view both mappings, including their abbreviations, and compare that to the abbreviation you parsed, and then choose either the earlier mapping, or the later mapping.

microfortnights?! Are you serious?

(by Howard Hinnant)

Well, kind of. The point of this article is to illustrate that no matter how crazy your units of time are, this library can handle it, and with style.

If you're dealing with quarter-second durations, or frame-durations of 1/60 second, or whatever, <chrono> can build the duration, and this library can format it. And all with very little effort. Let's say that you've got a time point consisting of a month, day, year, hour, minute, and microfortnight, just how hard is that to format out into human-readable format?!

Turns out not hard at all.

#include "date/date.h"
#include <iostream>

using fortnights =
    std::chrono::duration<date::weeks::rep,
                          std::ratio_multiply<std::ratio<2>, date::weeks::period>>;

using microfortnights =
    std::chrono::duration<std::int64_t, std::ratio_multiply<fortnights::period,
                                                            std::micro>>;

constexpr
inline
microfortnights
operator"" _ufn(unsigned long long x)
{
    return microfortnights{static_cast<microfortnights::rep>(x)};
}

int
main()
{
    using namespace date;
    using namespace std::chrono;
    std::cout << format("%F %T\n", sys_days{November/29/2016} + 15h + 13min + 35_ufn);
}

The first thing to do is build your chrono::duration that represents a microfortnight. This is best done by first building a fortnight, and then multiplying that by std::micro. This will build some weird chrono::duration with a period we don't really have to know, but turns out to be not that far off from a second.

To specify a sys_time in terms of this weird unit, you just do the usual addition. But instead of adding seconds you add microfortnights. Now it turns out that a microfortnight is exactly 1.2096 seconds (who knew?). But you don't have to concern yourself with this detail as long as you've correctly defined fortnights as 2 weeks as above, and microfortnights as a std::micro times fortnights as above.

Now you can just blindly add microfortnights{35} to your year/month/day hh::min timestamp as shown above, and ask format to format it with "%F %T". This will output the correct time stamp with fractional decimal seconds to exactly represent microfortnights:

2016-11-29 15:13:42.3360

If this library can do this so easily with something as crazy as microfortnights, it can handle your crazy time problem.

Find and set UNIX download folder thanks to xdg-user-dir

(by Coin²)

The library uses the path ~/Downloads by default. What if you use a different path for your download directory such as ~/Téléchargements or ~/Завантаження and you want your program to automatically find this path ? A solution is to use xdg_user_dir. The following code shows you how you can do it :

#include "date/date.h"
#include "date/tz.h"

#include <regex>
#include <iostream>

// Execute command, use a pipe and get the output of it (we cannot grab the output with std::system)
std::string exec_and_get_output(const char* cmd) 
{
    std::array<char, 128> buffer;
    std::string output;
    FILE *pipe(popen(cmd, "r"));
    if (!pipe) 
        throw std::runtime_error("Cannot execute command");
    while (!feof(pipe)) 
    {
        if (fgets(buffer.data(), 128, pipe) != nullptr)
            output += buffer.data();
    }
    pclose(pipe);
    return output;
}

std::string get_xdg_download_folder(const std::string& suffix = "tzdata_alt") 
{
    std::string download_folder = "~/Downloads";  // common download folder by default
    std::string xdg_command = "xdg-user-dir DOWNLOAD";
    std::string xdg_download_folder = exec_and_get_output(xdg_command.c_str());
    xdg_download_folder.erase // remove unnecessary new lines
    (
        std::remove(xdg_download_folder.begin(), xdg_download_folder.end(), '\n'), 
        xdg_download_folder.end()
    ); 
    // Verify that output from command is a folder so we can use it
    if (std::regex_match(xdg_download_folder, std::regex("^(/[^/ ]*)+/?$"))) 
    {
        download_folder = xdg_download_folder; 
    }
    return download_folder + '/' + suffix;
}

int main() 
{
    auto download_folder = get_xdg_download_folder();
    date::set_install(download_folder);

    // Your code which uses tz
}

Do not forget to set -DAUTO_DOWNLOAD=1 so that you download the tzdata in the first use.


Re-using unzoned date/time code for zoned date/time values (and vice versa)

(by Tai Meng)

The date/time libraries forbid implicit conversions between local_time and sys_time, and for good reason. If we type our variables using "auto" everywhere, sooner than later we'd run into a case where we'd be assigning a local_time to a sys_time or vice versa, and the compiler would catch this dangerous conversion. Thank you type safety!

However at times, to re-use existing code, we actually want to convert between local_time and sys_time. If this is what you wish to achieve, feel free to skip the story below, and read the solution that follows it.

<story begins>

Suppose you've written a layer of code to handle unzoned date/time addition with ISO durations of the form P1DT2H (a period of 1 day and 2 hours. Reference: https://en.wikipedia.org/wiki/ISO_8601#Durations). In your code, you would have used local_time everywhere as the date/time type.

Now a new user requirement comes in. Users want zoned date/time addition with ISO durations. You look at the code and begin to frown. Some of your functions return local_time, so the compiler forbids you to overload these functions with the return type sys_time. Ok, you can copy paste existing functions and rename them so that they return sys_time. But now you'd need to maintain two parallel branches of code, which would be more susceptible to defects. Wouldn't it be nice if you could strip the zoned date/time down to an unzoned date/time, re-use existing code, then re-append the timezone information to the output date/time value?

<story ends>

Here is how we can convert between local_time and sys_time:

const some_local_time unzonedTime{utcTime.time_since_epoch()};
const some_sys_time utcTime{unzonedTime.time_since_epoch()};

Where some_local_time and some_sys_time are template instantiations of local_time and sys_time.

Thoughts on reloading the IANA tzdb for long running programs

(by Howard Hinnant)

Let's start with this thought: Most programs don't need to stay up long enough to have to worry about the IANA time zone database changing out from under it. The IANA time zone database gets updated anywhere from every month to every season (3 months, might be 4). There is not a regular schedule for IANA time zone database updates. But it doesn't happen daily, or even weekly, and it does happen several times a year.

Next, if your program is up long enough and needs to worry about the latest IANA tzdb, and if multiple threads access the tzdb, then you have multithreading issues, and this lib doesn't provide an out-of-the-box fool-proof solution for you. The reason for that is that I don't want to impose a performance penalty on the vast majority of tzdb clients that don't need thread safe support for updatable tzdb databases. Most clients are fine with the IANA tzdb that exists when their program starts, and don't need to worry about it updating while their program is running.

Finally, if you are in the majority, and don't want to have to worry about the IANA tzdb updating while your program is running, all you have to do is never call date::reload_tzdb(). If you never call date::reload_tzdb(), then you can never have the thread safety issues this short article addresses. You're fine, stop reading now. Go grab a mojito.

Ok, now you're in the position of: I have several threads accessing the tzdb, and IANA has updated the database. I need to migrate to this new database, without stopping all of my threads. HELP!!!

Helping you is what this article addresses.

This library maintains a singly-linked list of tzdb as a singleton. The head of this list is what you implicitly access through common functions such as current_zone() or locate_zone("some time zone name"). If you never call date::reload_tzdb(), this list will always be of length 1, and you just don't have to worry about it. But if you call date::reload_tzdb(), and if this function succeeds in initializing a new tzdb, then it will atomically push_front a new tzdb onto this singleton list. date::reload_tzdb() does not invalidate references or pointers into any of the previous tzdb. They continue to exist and work just fine.

Here begins the strategy for your code...

One way to deal with this is to just let the singleton tzdb_list grow forever. That's ok with me if it is ok with you. It only grows by one database a few times a year. This guarantees that all of your threads which might be pointing in to older tzdb continue to work, although they might be using outdated data.

Another way to deal with this is to use a-priori knowledge that your threads won't continue to point in to a tzdb for longer than X amount of time, where X might be a minute, an hour, a day, a year, whatever. After whatever time has elapsed, you can just navigate the singleton tzdb_list and delete old databases.

Below is code that launches a thread that does nothing but wake up once a day and download a new tzdb if it is available, and set a timer to delete old tzdb if it has been 10 days since they were replaced.

#include "date/tz.h"
#include <atomic>
#include <iostream>
#include <thread>

// Checks for new tzdb once a day (early am local time).
// Allows old tzdb to hang around for 10 days and then deletes it
// Lock-free
void
tzdb_manager(std::atomic<bool>& run)
{
    using namespace std;
    using namespace std::chrono;
    using namespace date;
    // Get the current UTC time for today's local 02:00, approximation is ok
    auto tz = current_zone();
    auto check_at = zoned_time{tz,
                               floor<days>(zoned_time{tz, system_clock::now()}
                               .get_local_time()) + 2h, choose::latest}
                                   .get_sys_time();
    // Initialize clean-trigger for several years in the future
    auto clean_at = check_at + days{1000};
    while (run)
    {
        // Sleep until the wee hours of the morning
        this_thread::sleep_until(check_at);
        // If time to stop, stop
        if (!run)
            break;
        // Time for morning maintenance
        // First check if we need to pop_back the tzdb_list
        if (check_at >= clean_at)
        {
            auto& list = get_tzdb_list();
            auto i0 = list.begin();
            auto i1 = next(i0);
            // if the list has more than one tzdb
            if (i1 != list.end())
            {
                // pop_back the tzdb_list
                for (auto i2 = next(i1); i2 != list.end(); ++i0, ++i1, ++i2)
                    ;
                list.erase_after(i0);
                // if the new size is 1, clean again in a few years
                if (i0 == list.begin())
                    clean_at = check_at + days{1000};
                else  // otherwise new size > 1, clean again in 10 days
                    clean_at = check_at + days{10};
            }
            else  // list has only 1 tzdb, clean again in a few years
                clean_at = check_at + days{1000};
        }
        // If the remote version has been updated
        auto rv = remote_version();
        if (rv != get_tzdb().version)
        {
            // download, install and load the new tzdb
            if (remote_download(rv))
            {
                if (remote_install(rv))
                {
                    reload_tzdb();
                    // Schedule cleanup of the old tzdb for 10 days from now
                    clean_at = check_at + days{10};
                }
            }
            // if anything failed, we just try again tomorrow morning
        }
        // Maintenance done, go to sleep for a day
        check_at += days{1};
    }
}

The above assumes a build with DAUTO_DOWNLOAD=0. That is, the tz lib won't automatically download the IANA tzdb for you. If you want to build with DAUTO_DOWNLOAD=1 (which is the default on macOS and Linux), the code above can be simplified a bit by changing:

        auto rv = remote_version();
        if (rv != get_tzdb().version)
        {
            // download, install and load the new tzdb
            if (remote_download(rv))
            {
                if (remote_install(rv))
                {
                    reload_tzdb();
                    // Schedule cleanup of the old tzdb for 10 days from now
                    clean_at = check_at + days{10};
                }
            }
            // if anything failed, we just try again tomorrow morning
        }

to

        auto rv = remote_version();
        if (rv != get_tzdb().version)
        {
            // download, install and load the new tzdb
            reload_tzdb();
            // Schedule cleanup of the old tzdb for 10 days from now
            clean_at = check_at + days{10};
            // if anything failed, we just try again tomorrow morning
        }

It isn't the most friendly code to have to write. But on the other hand, it gives you complete discretion on your tzdb delete policy and doesn't penalize clients that have no need to update their tzdb database.

More complex (and expensive) policies could track reference count usage for each tzdb and delete only when that reference count drops to zero. It is completely implementable externally without any privileged hooks into "tz.h". This design is motivated by the "don't pay for what you don't use" philosophy.

Converting from Unix Time to date::year_month_day

(by tommy bandzaw)

When working with APIs that gives you a timestamp as a plain integer, signed or not, for example a Unix Time, and you want to use it with chrono/date/time calculations, here's what one can do (instead of using the time_t & localtime):

void callback(int unixtime)
{
    auto t = std::chrono::seconds(unixtime);
    std::chrono::system_clock::time_point tp(t);
    auto d = date::floor<date::days>(tp);
    date::year_month_day ymd(d);
    date::month_day md(ymd.month(), ymd.day());
}

CC BY LogoThis work is licensed under a Creative Commons Attribution 4.0 International License.

Clone this wiki locally