Examples and Recipes

Howard Hinnant edited this page Feb 4, 2017 · 70 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 "tz.h"
#include <iostream>

int
main()
{
    std::cout << date::make_zoned(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. make_zoned pairs the current UTC time with the computer's current local time zone, producing a zoned_time. 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).

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 "tz.h"
#include <iostream>

int
main()
{
    std::cout << date::make_zoned("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.

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 "tz.h"
#include <iostream>

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

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. make_time 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. using directives make the code a little more readable:

using namespace date;
using namespace std::chrono;
std::cout << make_time(floor<minutes>(sy.get_local_time() - la.get_local_time())) << '\n';

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 "tz.h"
#include <iostream>

int
main()
{
    using namespace std::chrono;
    using namespace date;
    auto ny = make_zoned("America/New_York", local_days{jul/8/2016} + 9h);
    auto moscow = make_zoned("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:

auto moscow = make_zoned("Europe/Moscow", local_days{8_d/jul/2016} + 16h);
auto ny = make_zoned("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/jul/8} + 13h;
auto ny = make_zoned("America/New_York", utc);
auto moscow = make_zoned("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:

auto ny = make_zoned("America/New_York", sys_days{jul/8/2016} + 13h);
auto moscow = make_zoned("Europe/Moscow", ny);

In any event, the output is still:

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

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;
    auto zt = make_zoned(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().count() << "ms\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:

auto zt = make_zoned(locate_zone("America/New_York"), local_days{jan/15/2016} + 3h);
std::cout << zt << " is " << since_local_midnight(zt).count() << "ms 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{sun[2]/mar/2016} + 3h;
std::cout << zt << " is " << since_local_midnight(zt).count() << "ms 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{jul/15/2016} + 3h;
std::cout << zt << " is " << since_local_midnight(zt).count() << "ms 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{sun[1]/nov/2016} + 3h;
std::cout << zt << " is " << since_local_midnight(zt).count() << "ms 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 "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

(by ecorm)

See http://stackoverflow.com/questions/31711782.

using namespace std::chrono;
using namespace date;
// Component values (as obtained from an UI form, for example)
int y=2015, m=7, d=30, h=12, min=34, s=56;
auto ymd = year(y)/m/d; // Yields a year_month_day type
if (!ymd.ok())
    throw std::runtime_error("Invalid date");
system_clock::time_point tp = sys_days(ymd) +
                           hours(h) + minutes(min) + seconds(s);

Alternatively, using struct tm to hold the components:

// 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);

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

(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);
auto ymd = year_month_day(daypoint);   // calendar date
auto tod = make_time(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();
    auto tod = make_time(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  = unsigned(ymd.day());
    result.tm_mon   = unsigned(ymd.month()) - 1u; // Zero-based!
    result.tm_year  = int(ymd.year()) - 1900;
    result.tm_wday  = unsigned(weekday);
    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 << "-" << t.tm_mon << "-" << t.tm_day << " ";
          << 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/dec/32 will become 2016_y/jan/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

The specifications for these functions do not yet guarantee this "normalization behavior." But the implementation has been thoroughly tested for this behavior and the specification will be updated soon.

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 = jan; true; ++m)
    {
        auto d = fri[5]/m/y;
        if (d.ok())
        {
            dates[n] = year_month_day{d};
            ++n;
        }
        if (m == dec)
            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 = fri[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. mon - sun is days{1} since mon is always 1 day after sun. sun - mon is days{6} since sun is always 6 days after mon.

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 = to_utc_time(sys_days{y/jan/1}) + us;
    auto sys = to_sys_time(utc);
    auto dp = floor<days>(sys);
    auto d = dp - sys_days{1958_y/jan/1};
    us = utc - to_utc_time(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/oct/2013;
auto d2 = 30_d/oct/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;
auto time = date::make_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.h"
#include "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
    auto birthday = make_zoned("America/Los_Angeles",
                               local_days{apr/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 make_zoned. 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(make_zoned(zone,
                local_days{apr/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:

auto Day2Gs = make_zoned(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/jan/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/jan/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 "tz.h"
#include <iostream>

int
main()
{
    using namespace std::chrono;
    using namespace date;
    auto base = make_zoned("America/New_York", local_days{mar/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 "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.push_back(make_zoned(&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.push_back(make_zoned(&z, tp));
            break;
        case local_info::ambiguous:
            if (i.first.abbrev == abbrev)
                results.push_back(make_zoned(&z, tp, choose::earliest));
            else if (i.second.abbrev == abbrev)
                results.push_back(make_zoned(&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 make_zoned 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 "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{jan/1/year::min()} + 0s;
        auto end   = sys_days{jan/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 "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/jan/15});
            auto info2 = z.get_info(sys_days{y/jul/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/jan/1} - sys_days{1899_y/dec/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/jan/1} - sys_days{1899_y/dec/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/dec/30) << '\n';
    std::cout << (int)to_TDate(1900_y/jan/1) << '\n';
    std::cout << (int)to_TDate(1899_y/dec/29) << '\n';
    std::cout << (int)to_TDate(1996_y/jan/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/jan/1} - sys_days{1899_y/dec/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/jan/1} - sys_days{1899_y/dec/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/dec/30} + 0min) << '\n';
    std::cout << (double)to_TDateTime(sys_days{1900_y/jan/1} + 18h + 0min) << '\n';
    std::cout << (double)to_TDateTime(sys_days{1899_y/dec/29} + 6h + 0min) << '\n';
    std::cout << (double)to_TDateTime(sys_days{1996_y/jan/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/jan/1} - sys_days{year{-4713}/nov/24})};
}

QDate
to_QDate(date::sys_days sd)
{
    using namespace date;
    return QDate::fromJulianDay((sd.time_since_epoch() +
        (sys_days{1970_y/jan/1} - sys_days{year{-4713}/nov/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;
}

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.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", sys_days{ym/1}) << right;
        break;
    case 1:
        {
        // Output weekday names title
        auto sd = sys_days{ym/firstdow[1]};
        for (auto const esd = sd + weeks{1}; sd < esd; sd += days{1})
        {
            auto d = format(os.getloc(), "%a", sd);
            d.resize(2);
            os << ' ' << d;
        }
        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 << setw(3) << unsigned(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 << setw(3) << unsigned(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::sun)
{
    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 nth firstdow 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::sun);

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, mon);

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 "tz.h"

  void 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;
     parse(inputStream, "%F %T %Ez %Z", tp, tz_name);

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

     auto zt = make_zoned(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';
  }

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.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{nov/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.


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