
I had just completed writing date, which is a
library for extending <chrono> into the realm of calendars, and I was
looking around for the most challenging date time problem I could find with which I could
demonstrate the power of this new library. "I know," I said to myself, "I'll handle all
of the world's time zones, and maybe even leap seconds!" Thus began my journey into a
rabbit hole which I knew existed, but had never truly appreciated the intricacies of.
This library adds timezone and leap second support to this date
library. This is a separate library from date
because many clients of date do not need timezone
nor leap second support, and this support does not come for free (though the cost is quite
reasonable).
This library is a complete parser of the IANA Time Zone Database. This database contains timezone information that represents the history of local time for many representative locations around the globe. It is updated every few months to reflect changes made by political bodies to time zone boundaries, UTC offsets, and daylight-saving rules. The database also maintains a list of leap seconds from 1972 through the present.
The IANA Time Zone Database contains four specific types of data:
Zone: A geographic location with a human-readable name (e.g. "America/New_York") which specifies the offset from UTC and an abbreviation for the zone. This data includes daylight saving rules, if applicable, for the zone. This data is not only the rules currently in effect for the region, but also includes specifications dating back to at least 1970, and in most cases dating back to the mid 1800's (when uniform time was first introduced across regions larger than individual towns and cities).
Rule: A specification for a single daylight-saving rule. This helps implement and consolidate the specifications of Zones.
link: This is an alternative name for a Zone.
leap: The date of the insertion of a leap second.
The library documented herein provides access to all of this data, and offers
efficient and convenient ways to compute with it. And this is all done based on the date library, which in turn is based on the C++11/14
<chrono> library. So once you've learned those fundamental libraries,
the learning curve for this library is greatly eased.
Everything documented below is in namespace date. Explicit references to
this namespace in example code below is intentionally omitted in the hopes of reducing
verbosity.
One of the first things people want to do is find out what the current local time it is. Here is a complete program to print out the local time in human readable format:
#include "tz.h"
#include <iostream>
int
main()
{
using namespace date;
using namespace std::chrono;
auto t = make_zoned(current_zone(), system_clock::now());
std::cout << t << '\n';
}
This just output for me:
2016-05-14 18:33:24.205124 EDT
There are some noteworthy points about this program:
This is a <chrono>-based system. The current time is
found with std::chrono::system_clock::now().
The computer's current local time zone is not assumed. If anything is assumed that
would be UTC, since this is the time zone that system_clock tracks
(unspecified but de facto standard).
Specifying you want to convert system_clock::time_points to the
current local time zone is as easy as calling date::current_zone()
and pairing that with a system_clock::time_point using
date::make_zoned. This creates a zoned_time.
This zoned_time maintains whatever precision it was given. On my
platform system_clock::now() has microseconds precision, so in this
example, t has microseconds precision as well.
Then t is simply streamed out. By default the output
represents all of the precision it is given.
Everything about the above program can be customized: the precision, the formatting, and the time zone. But by default, things just work, and don't throw away information.
For example let's say we wanted to limit the precision to milliseconds. This can
be done by inserting floor<milliseconds> in one place. This
makes t have just a precision of milliseconds
and that is reflected in the streaming operator with no further effort:
auto t = make_zoned(current_zone(), floor<milliseconds>(system_clock::now())); std::cout << t << '\n'; // 2016-05-14 18:33:24.205 EDT
Seconds precision is just as easy:
auto t = make_zoned(current_zone(), floor<seconds>(system_clock::now())); std::cout << t << '\n'; // 2016-05-14 18:33:24 EDT
The entire strftime / time_put formatting capability is
also at your fingertips (and at any precision):
auto t = make_zoned(current_zone(), system_clock::now());
std::cout << format("%a, %b %d, %Y at %I:%M %p %Z", t) << '\n';
// Sat, May 14, 2016 at 06:33 PM EDT
Using any std::locale your OS supports:
auto t = make_zoned(current_zone(), floor<seconds>(system_clock::now()));
std::cout << format(locale("de_DE"), "%a, %b %d, %Y at %T %Z", t) << '\n';
// Sa, Mai 14, 2016 at 18:33:24 EDT
From the previous section:
Hmm... German locale in an American time zone.
We can fix that easily too:
auto zone = locate_zone("Europe/Berlin");
auto t = make_zoned(zone, floor<seconds>(system_clock::now()));
std::cout << format(locale("de_DE"), "%a, %b %d, %Y at %T %Z", t) << '\n';
// So, Mai 15, 2016 at 00:33:24 CEST
The date::locate_zone() function looks up the IANA time zone with the name
"Europe/Berlin" and returns a const time_zone* which has no ownership
issues and can be freely and cheaply copied around. It is not possible for
locate_zone() to return nullptr, though it might throw
an exception if pushed far enough (e.g. locate_zone("Disney/Mickey_Mouse")).
You can also call make_zoned with the time zone name right in the call:
auto t = make_zoned("Europe/Berlin", floor<seconds>(system_clock::now()));
The first way is very slightly more efficient if you plan on using zone
multiple times since it then only has to be looked up once.
time_zone from one time zone to another?
So far we've only looked at converting from system_clock::now() to
a local, or specific time zone. We've used make_zoned with the
first argument being either current_zone() or a specification for
some other time zone, and the second argument being a
system_clock::time_point. So far so good.
But now I have a video-conference meeting on the first Monday of May, 2016 at 9am New York time. I need to communicate that meeting with partners in London and Sydney. And the computation is taking place on a computer in New Zealand (or some other unrelated time zone). What does that look like?
#include "tz.h"
#include <iostream>
int
main()
{
using namespace date::literals;
using namespace std::chrono_literals;
auto meet_nyc = make_zoned("America/New_York", date::local_days{mon[1]/may/2016} + 9h);
auto meet_lon = make_zoned("Europe/London", meet_nyc);
auto meet_syd = make_zoned("Australia/Sydney", meet_nyc);
std::cout << "The New York meeting is " << meet_nyc << '\n';
std::cout << "The London meeting is " << meet_lon << '\n';
std::cout << "The Sydney meeting is " << meet_syd << '\n';
}
The output is the following. But before you forward it, send a generous bonus to the guys in Australia.
The New York meeting is 2016-05-02 09:00:00 EDT The London meeting is 2016-05-02 14:00:00 BST The Sydney meeting is 2016-05-02 23:00:00 AEST
The first time, meet_nyc is a pairing of a time zone ("America/New_York")
with a local time (mon[1]/may/2016 at 09:00). Note that this
input is exactly reflected in the output:
The New York meeting is 2016-05-02 09:00:00 EDT
The next line creates meet_lon with the zoned_time
meet_nyc and a new time zone: "Europe/London". The effect of this pairing
is to create a time_point with the exact same UTC time point, but
associated with a different time_zone for localization purposes. That is,
after this "converting construction", an invariant is that
meet_lon.get_sys_time() == meet_nyc.get_sys_time(), even though these
two objects refer to different time zones.
The same recipe is followed for creating meet_syd. The default formatting
for these zoned_times is to output the local date and time followed
by the current time zone abbreviation.
Summary: zoned_time is a pairing of local or UTC time with a time_zone.
The result is a well-specified point in time. And it carries with it the ability to
serve as a translator to any other time_point which carries time zone
information (to any precision).
local_time vs sys_time
Let's say I want to refer to the New Years Day party at 2017-01-01 00:00:00. I don't
want to refer to a specific party at some geographical location. I want to refer to
the fact that this moment is celebrated in different parts of the world according to
local times. This is called a local_time.
auto new_years = local_time<days>{2017_y/jan/1} + 0h + 0m + 0s;
A local_time<D> can be created with any duration D and
is a std::chrono::time_point except that
local_time<D>::clock has no now() function. There is
no time zone associated with local_time.
local_timeis not the time associated with the current local time the computer is set to.
local_time is a time associated with an as yet
unspecified time zone. Only when you pair a local_time with a
time_zone do you get a concrete point in time that can be converted
to UTC and other time zones: a zoned_time.
There also exist convenience type aliases:
using local_seconds = local_time<std::chrono::seconds>; using local_days = local_time<days>;
In summary: When is 1min after New Years 2017?
auto t = local_days{jan/1/2017} + 1min;
cout << t << '\n'; // 2017-01-01 00:01
When is 1min after New Years 2017 UTC?
auto t = sys_days{jan/1/2017} + 1min;
cout << t << '\n'; // 2017-01-01 00:01
This effectively means that year_month_day is also ambiguous as to
whether it refers to a local (timezone-less) time or to UTC. You have to
specify which when you use it. But that is the nature of how people use dates
(points in time with days precision). "There will be a celebration on New Years."
In many contexts the time zone is intentionally left unspecified.
When is 1min after New Years 2017 in New York?
zoned_seconds t{"America/New_York", local_days{jan/1/2017} + 1min};
cout << t << '\n'; // 2017-01-01 00:01:00 EST
What time will it be in New York when it is 1min after New Years 2017 UTC?
zoned_seconds t{"America/New_York", sys_days{jan/1/2017} + 1min};
cout << t << '\n'; // 2016-12-31 19:01:00 EST
We now have 5 concepts and their associated types:
Calendars: These are day-precision time points that are typically field structures (multiple fields that create a unique "name" for a day).
Example calendars include year_month_day and
year_month_weekday. Other examples could include the ISO
week-based calendar, the Julian calendar, the Islamic calendar, the Hebrew
calendar, the Chinese calendar, the Mayan calendar, etc.
Calendars can convert to and from both sys_days and
local_days. These two conversions involve identical arithmetic, but
have semantic differences.
Once these conversions are implemented, the calendars are not only interoperable
with zoned_time, but are also interoperable with each other. That
is dates in the Chinese calendar can easily be converted to or from dates in the
Mayan calendar even though these two calendars have no knowledge of the other.
Disclaimer: "date.h" provides only the year_month_day and
year_month_weekday calendars.
sys_time: This is a serial time point and a
std::chrono::time_point of arbitrary precision. It has
sys_seconds and sys_days convenience precisions.
sys_time is a time_point associated with the return of
system_clock::now() and represents
Unix Time which very
closely approximates UTC.
local_time: This is a serial time point and a
std::chrono::time_point of arbitrary precision. It has
local_seconds and local_days convenience precisions.
local_time is a time_point associated with no time
zone, and no clock::now(). It is the void* of
time_points.
time_zone: This represents a specific geographical area, and all
time zone related information for this area over all time. This includes a
name for the area, and for any specific point in time, the UTC offset, the
abbreviation, and additional information.
zoned_time: This is a pairing of a time_zone and a
sys_time (of precision seconds or finer). It can also be
equivalently viewed as a pairing of a time_zone and a
local_time. Once constructed it represents a valid point in time,
and the time_zone, sys_time and
local_time can all be extracted. There exists a
zoned_seconds convenience precision.
time_zones are retrieved from a time zone database. The database
also holds information about leap seconds. To make computing with leap
seconds easier, there is a clock that takes leap seconds into account:
utc_clock. This clock has an associated family of time points
called utc_time.
Full formatting and parsing facilities are available with strftime-like
formatting strings.
Interesting things can happen to the apparent time when you travel across the globe at high speeds. So departure and arrival times of airplane flights make for good examples involving time zone arithmetic.
#include "tz.h"
#include <iostream>
int
main()
{
using namespace std::chrono_literals;
using namespace date;
auto departure = make_zoned("America/New_York", local_days{dec/30/1978} + 12h + 1min);
auto flight_length = 14h + 44min;
auto arrival = make_zoned("Asia/Tehran", departure.get_sys_time() + flight_length);
std::cout << "departure NYC time: " << departure << '\n';
std::cout << "flight time is " << make_time(flight_length) << '\n';
std::cout << "arrival Tehran time: " << arrival << '\n';
}
The output of the above program is:
departure NYC time: 1978-12-30 12:01:00 EST flight time is 14:44 arrival Tehran time: 1978-12-31 11:45:00 IRST
The departure time is formed by transforming the local calendar date time into a
local_time and pairing that with the "America/New_York"
time_zone to form a zoned_time. The flight time is
just an ordinary chrono::duration.
The arrival time is formed by retrieving the departure time in terms of
sys_time, adding the length of the flight, and pairing that
sys_time with the "Asia/Tehran" time_zone to form a
zoned_time.
By doing the arithmetic (addition of the flight time) in the UTC (well system) time zone, we do not have to worry about things like daylight savings time, or other political changes to the either UTC offset. For example if we change one line to look at the same flight 24 hours later:
auto departure = make_zoned("America/New_York", local_days{dec/31/1978} + 12h + 1min);
Then the output changes to:
departure NYC time: 1978-12-31 12:01:00 EST flight time is 14:44 arrival Tehran time: 1979-01-01 11:15:00 IRST
Now we have the flight arriving 30min earlier. This is because the time zone "Asia/Tehran" undergoes an offset change while the plane is in the air, shifting its UTC offset to 30min earlier. Is this the final word on this example? Almost. If accuracy down to the second is required (it is not for a flight arrival), then additional effort needs to be expended. Because there was also a leap second insertion while the plane was in the air. This can be taken into account with the following code:
#include "tz.h"
#include <iostream>
int
main()
{
using namespace std::chrono_literals;
using namespace date;
auto departure = make_zoned("America/New_York", local_days{dec/31/1978} + 12h + 1min);
auto departure_utc = to_utc_time(departure.get_sys_time());
auto flight_length = 14h + 44min;
auto arrival = make_zoned("Asia/Tehran", to_sys_time(departure_utc + flight_length));
std::cout << "departure NYC time: " << departure << '\n';
std::cout << "flight time is " << make_time(flight_length) << '\n';
std::cout << "arrival Tehran time: " << arrival << '\n';
}
This is just like the previous example except that the arithmetic (departure
time + flight length) is done in utc_time instead of
sys_time. To accomplish this, there is a conversion from
sys_time to utc_time before the arithmetic, and
another conversion from utc_time to sys_time after the
arithmetic. And the result changes to:
departure NYC time: 1978-12-31 12:01:00 EST flight time is 14:44 arrival Tehran time: 1979-01-01 11:14:59 IRST
A common task in dealing with dates and times is converting from one string format to another. This library is extremely flexible in handling this task. As an example, let's say that you need to convert strings that look like this:
Sun Sep 16 01:03:52 -0500 1973
Into strings that look like this:
1973-09-16T06:03:52.000Z
That is, given a local time with UTC offset, you need to not only update the format to something more modern, but it also has to be converted to the UTC timezone and to a precision of milliseconds. The code to do this is quite straight forward:
std::string
convert(const std::string& input)
{
using namespace std;
using namespace std::chrono;
using namespace date;
istringstream stream{input};
sys_time<milliseconds> t;
parse(stream, "%a %b %d %T %z %Y", t);
if (stream.fail())
throw runtime_error("failed to parse " + input);
return format("%FT%TZ", t);
}
Let's walk through this:
First, date::parse works with istreams so you can parse from
files, from strings, or anything else that is an istream.
Second, while we don't need to parse to a precision of milliseconds, we need to
format to that precision. It is easy just to parse into a
milliseconds-precision sys_time so that we can then just format it
back out with no change. If we needed to parse at finer precision than
formatting, then we would need to parse at the higher precision, truncate it (by
some rounding mode — truncate, floor,
ceil or round), and then format the truncated value.
To have the parse interpret the string as a local time offset by the
UTC offset, we need to ask for a sys_time to be parsed, and use
the %z in the proper location. The parse function will
then subtract the UTC offset to give us the proper sys_time value.
If parse fails to find everything in the parse/format string,
exactly as specified, it will set failbit in the istream.
Finally, once we know we have a successfully parsed
sys_time<milliseconds> it is a very simple matter to format
it back out in whatever format is desired. As confirmed in the
Reference, %S and %T are
sensitive to the precision of the time point argument, and so there is no need
for extension formatting flags to indicate fractional seconds. %S
and %T just work.
Everything specified below is in namespace date, and accessed via the
header "tz.h".
The following data structure is the time zone database, and the following functions access it.
struct TZ_DB { std::string version; std::vector<time_zone> zones; std::vector<link> links; std::vector<leap> leaps; std::vector<Rule> rules; };The
TZ_DB database is a singleton. And access to it is read-only, except for reload_tzdb()which re-initializes it. Eachvectoris sorted to enable fast lookup. You don't have to explicitly program binary search lookups on it. That is handled by the API. But you can explicitly iterate over and inspect this database. And knowing that it is sorted may be of benefit to your inspection logic.All information in the IANA time zone database is represented in the above
TZ_DBdata structure, except for the comments in the database. Thus it is up to you, the client of this library, to decide what to do with this data. This library makes it especially easy and convenient to extract the data in the way that is most commonly used (e.g. time conversions among time zones). But it represents all of the data, and hides none of it.const TZ_DB& get_tzdb();Effects: If this is the first access to the database, will initialize the database. If
tz.cppwas compiled with the configuration macroAUTO_DOWNLOAD == 1, initialization will include checking the IANA website for the latest version, and downloading the latest version if your local version is out of date, or doesn't exist at the location referred to by theinstallconfiguration variable intz.cpp. Iftz.cppwas compiled withAUTO_DOWNLOAD == 0, you will have to download and decompress the IANA database from the IANA website and place it at the location referred to by theinstallconfiguration variable.
AUTO_DOWNLOAD == 1requires linkingtz.cpptolibcurl.Returns: A
constreference to the database.Thread Safety: It is safe to call this function from multiple threads at one time. There will be no race to initialize the singleton database as long as your compiler implements threadsafe function-local statics as specified by C++11.
Throws:
std::runtime_errorif for any reason a reference can not be returned to a validTZ_DB.const time_zone* locate_zone(const std::string& tz_name);Effects: Calls
get_tzdb()which will initialize the timezone database if this is the first reference to the database.Returns: If a
time_zoneis found for whichname() == tz_name, returns a pointer to thattime_zone. Otherwise if alinkis found wheretz_name == link.name(), then a pointer is returned to thetime_zonefor whichzone.name() == link.target()[Note: Alinkis an alternative name for atime_zone. — end note]Throws: Any exception propagated from
get_tzdb(). If aconst time_zone*can not be found as described in the Returns clause, throws astd::runtime_error. [Note: On non-exceptional return, the return value is always a pointer to a validtime_zone. — end note]const time_zone* current_zone();Effects: Callslocate_zone()which will initialize the timezone database if this is the first reference to the database.Returns: A
const time_zone*referring to the time zone which your computer has set as its local time zone.Throws: Any exception propagated from
locate_zone(). [Note: On non-exceptional return, the return value is always a pointer to a validtime_zone. — end note]const TZ_DB& reload_tzdb();Effects:
If If
tz.cppwas compiled with the configuration macroAUTO_DOWNLOAD == 1, this function first checks the latest version at the IANA website. If the IANA website is unavailable, or if the latest version is already installed, there are no effects. Otherwise, a new version is available. It is downloaded and installed, and then the program re-initializes theTZ_DBsingleton from the new disk files.If
tz.cppwas compiled with the configuration macroAUTO_DOWNLOAD == 0, this function re-initializes theTZ_DBsingleton from the disk files. You can manually replace the database without ill-effects after your program has calledget_tzdb()and before it callsreload_tzdb(), as there is no access to the files on disk between the first call toget_tzdb()and subsequent calls toreload_tzdb().Returns: A
constreference to the database.Thread Safety: This function is not thread safe. You must provide your own synchronization among threads accessing the time zone database to safely use this function. If this function re-initializes the database (as it always does when
AUTO_DOWNLOAD == 0), all outstandingconst time_zone*are invalidated (including those held withinzoned_timeobjects). And afterwards, all outstandingsys_infomay hold obsolete data.Throws:
std::runtime_errorif for any reason a reference can not be returned to a validTZ_DB.The following functions are available only if you compile with the configuration macro
HAS_REMOTE_API == 1. Use of this API requires linking tolibcurl.AUTO_DOWNLOAD == 1requiresHAS_REMOTE_API == 1. You will be notified at compile time ifAUTO_DOWNLOAD == 1andHAS_REMOTE_API == 0. IfHAS_REMOTE_API == 1, thenAUTO_DOWNLOADdefaults to1, otherwiseAUTO_DOWNLOADdefaults to0. On Windows,HAS_REMOTE_APIdefaults to0. Everywhere else it defaults to1. This is becauselibcurlcomes preinstalled everywhere but Windows, but it is available for Windows.[Note: Even with
AUTO_DOWNLOAD == 1, there are no thread-safety issues with this library unless one of the following functions are explicitly called by your code:const TZ_DB& reload_tzdb(); bool remote_download(const std::string& version); bool remote_install(const std::string& version);Once your program has initialized the
TZ_DBsingleton, that singleton can never be changed without explicit use ofreload_tzdb(). — end note]std::string remote_version();Returns: The latest database version number from the IANA website. If the IANA website can not be reached, or if it can be reached but the latest version number is unexpectedly not available, the empty string is returned.
Note: If non-empty, this can be compared with
get_tzdb().versionto discover if you have the latest database installed.bool remote_download(const std::string& version);Effects: If
version == remote_version()this function will download the compressed tar file holding the latest time zone database from the IANA website. The tar file will be placed at the location indicated by theinstallconfiguration variable intz.cpp.Returns:
trueif the database was successfully downloaded, elsefalse.Thread safety: If called by multiple threads, there will be a race on the creation of the tar file at
install.bool remote_install(const std::string& version);Effects: If
versionrefers to the file successfully downloaded byremote_download()this function will remove the existing time zone database atinstall, then extract a new database from the tar file and place it atinstall, and finally will delete the tar file.This function does not cause your program to re-initialize itself from this new database. In order to do that, you must call
reload_tzdb()(orget_tzdb()if the database has yet to be initialized). Iftz.cppwas compiled withAUTO_DOWNLOAD == 1, thenreload_tzdb()uses this API to check if the database is out of date, and reinitializes it with a freshly downloaded database only if it needs to. Indeed, ifAUTO_DOWNLOAD == 1there is never any need to callremote_download()orremote_install()explicitly. You can just callreload_tzdb()instead. This API is only exposed so that you can take care of this manually if desired (HAS_REMOTE_API == 1 && AUTO_DOWNLOAD == 0).Returns:
trueif the database was successfully replaced by the tar file , elsefalse.Thread safety: If called by multiple threads, there will be a race on the creation of the new database at
install.Everything else in this library concerns read-only access to this database, and intuitive ways to compute with that information, even while being oblivious to the fact that you are accessing a database.
The entire database on disk occupies less than half of the disk space consumed by an average Beatles song. Don't sweat multiple copies of it. It will easily fit in your smart toaster.
chooseFor some conversions from
local_timeto asys_time,choose::earliestorchoose::latestcan be used to convert a non-existent or ambiguouslocal_timeinto asys_time, instead of throwing an exception.enum class choose {earliest, latest};
nonexistent_local_time
nonexistent_local_timeis thrown when one attempts to convert a non-existentlocal_timeto asys_timewithout specifyingchoose::earliestorchoose::latest.class nonexistent_local_time : public std::runtime_error { public: // Construction is undocumented };[Example:
#include "tz.h" #include <iostream> int main() { using namespace date; using namespace std::chrono_literals; try { auto zt = make_zoned("America/New_York", local_days{sun[2]/mar/2016} + 2h + 30min); } catch (const nonexistent_local_time& e) { std::cout << e.what() << '\n'; } }Which outputs:
2016-03-13 02:30:00 is in a gap between 2016-03-13 02:00:00 EST and 2016-03-13 03:00:00 EDT which are both equivalent to 2016-03-13 07:00:00 UTC— end example:]
ambiguous_local_time
ambiguous_local_timeis thrown when one attempts to convert an ambiguouslocal_timeto asys_timewithout specifyingchoose::earliestorchoose::latest.class ambiguous_local_time : public std::runtime_error { public: // Construction is undocumented };[Example:
#include "tz.h" #include <iostream> int main() { using namespace date; using namespace std::chrono_literals; try { auto zt = make_zoned("America/New_York", local_days{sun[1]/nov/2016} + 1h + 30min); } catch (const ambiguous_local_time& e) { std::cout << e.what() << '\n'; } }Which outputs:
2016-11-06 01:30:00 is ambiguous. It could be 2016-11-06 01:30:00 EDT == 2016-11-06 05:30:00 UTC or 2016-11-06 01:30:00 EST == 2016-11-06 06:30:00 UTC— end example:]
sys_infoThis structure can be obtained from the combination of a
time_zoneand either asys_time, orlocal_time. It can also be obtained from azoned_timewhich is effectively apairof atime_zoneandsys_time.This structure represents a lower-level API. Typical conversions from
sys_timetolocal_timewill use this structure implicitly, not explicitly.struct sys_info { sys_seconds begin; sys_seconds end; std::chrono::seconds offset; std::chrono::minutes save; std::string abbrev; };The
beginandendfields indicate that for the associatedtime_zoneandtime_point, theoffsetandabbrevare in effect in the range[begin, end). This information can be used to efficiently iterate the transitions of atime_zone.The
offsetfield indicates the UTC offset in effect for the associatedtime_zoneandtime_point. The relationship betweenlocal_timeandsys_timeis:offset = local_time - sys_timeThe
savefield is "extra" information not normally needed for conversion betweenlocal_timeandsys_time. Ifsave != 0min, thissys_infois said to be on "daylight saving" time, andoffset - savesuggests what thistime_zonemight use if it were off daylight saving. However this information should not be taken as authoritative. The only sure way to get such information is to query thetime_zonewith atime_pointthat returns ansys_infowheresave == 0min. There is no guarantee whattime_pointmight return such ansys_infoexcept that it is guaranteed not to be in the range[begin, end)(ifsave != 0minfor thissys_info).The
abbrevfield indicates the current abbreviation used for the associatedtime_zoneandtime_point. Abbreviations are not unique among thetime_zones, and so one can not reliably map abbreviations back to atime_zoneand UTC offset.You can stream out a
sys_info:std::ostream& operator<<(std::ostream& os, const sys_info& r);
local_infoThis structure represents a lower-level API. Typical conversions from
local_timetosys_timewill use this structure implicitly, not explicitly.struct local_info { enum {unique, nonexistent, ambiguous} result; sys_info first; sys_info second; };When a
local_timetosys_timeconversion is unique,result == unique,firstwill be filled out with the correctsys_infoandsecondwill be zero-initialized. If the conversion stems from a nonexistentlocal_timethenresult == nonexistent,firstwill be filled out with thesys_infothat ends just prior to thelocal_timeandsecondwill be filled out with thesys_infothat begins just after thelocal_time. If the conversion stems from an ambiguouslocal_timethenresult == ambiguous,firstwill be filled out with thesys_infothat ends just after thelocal_timeandsecondwill be filled out with thesys_infothat starts just before thelocal_time.You can stream out a
local_info:std::ostream& operator<<(std::ostream& os, const local_info& r);
time_zoneA
time_zonerepresents all time zone transitions for a specific geographic area.time_zoneconstruction is undocumented, and done for you during the database initialization. You can gainconstaccess to atime_zonevia functions such aslocate_zone.class time_zone { public: time_zone(const time_zone&) = delete; time_zone& operator=(const time_zone&) = delete; const std::string& name() const; template <class Duration> sys_info get_info(sys_time<Duration> st) const; template <class Duration> local_info get_info(local_time<Duration> tp) const; template <class Duration> sys_time<typename std::common_type<Duration, std::chrono::seconds>::type> to_sys(local_time<Duration> tp) const; template <class Duration> sys_time<typename std::common_type<Duration, std::chrono::seconds>::type> to_sys(local_time<Duration> tp, choose z) const; template <class Duration> local_time<typename std::common_type<Duration, std::chrono::seconds>::type> to_local(sys_time<Duration> tp) const; }; bool operator==(const time_zone& x, const time_zone& y); bool operator!=(const time_zone& x, const time_zone& y); bool operator< (const time_zone& x, const time_zone& y); bool operator> (const time_zone& x, const time_zone& y); bool operator<=(const time_zone& x, const time_zone& y); bool operator>=(const time_zone& x, const time_zone& y); std::ostream& operator<<(std::ostream& os, const time_zone& z)const std::string& time_zone::name() const;Returns: The name of the
time_zone.Example: "America/New_York".
Note: Here is an unofficial list of
time_zonenames: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones.template <class Duration> sys_info time_zone::get_info(sys_time<Duration> st) const;Returns: A
sys_infoifor whichstis in the range[i.begin, i.end).template <class Duration> local_info time_zone::get_info(local_time<Duration> tp) const;Returns: A
local_infofortp.template <class Duration> sys_time<typename std::common_type<Duration, std::chrono::seconds>::type> time_zone::to_sys(local_time<Duration> tp) const;Returns: A
sys_timethat is at least as fine asseconds, and will be finer if the argumenttphas finer precision. Thissys_timeis the UTC equivalent oftpaccording to the rules of thistime_zone.Throws: If the conversion from
tpto asys_timeis ambiguous, throwsambiguous_local_time. If the conversion fromtpto asys_timeis nonexistent, throwsnonexistent_local_time.template <class Duration> sys_time<typename std::common_type<Duration, std::chrono::seconds>::type> time_zone::to_sys(local_time<Duration> tp, choose z) const;Returns: A
sys_timethat is at least as fine asseconds, and will be finer if the argumenttphas finer precision. Thissys_timeis the UTC equivalent oftpaccording to the rules of thistime_zone. If the conversion fromtpto asys_timeis ambiguous, returns the earliersys_timeifz == choose::earliest, and returns the latersys_timeifz == choose::latest. If thetprepresents a non-existent time between two UTCtime_points, then the two UTCtime_points will be the same, and that UTCtime_pointwill be returned.template <class Duration> local_time<typename std::common_type<Duration, std::chrono::seconds>::type> time_zone::to_local(sys_time<Duration> tp) const;Returns: The
local_timeassociated withtpand thistime_zone.bool operator==(const time_zone& x, const time_zone& y);Returns:
x.name() == y.name().bool operator!=(const time_zone& x, const time_zone& y);Returns:
!(x == y).bool operator<(const time_zone& x, const time_zone& y);Returns:
x.name() < y.name().bool operator>(const time_zone& x, const time_zone& y);Returns:
y < x.bool operator<=(const time_zone& x, const time_zone& y);Returns:
!(y < x).bool operator>=(const time_zone& x, const time_zone& y);Returns:
!(x < y).std::ostream& operator<<(std::ostream& os, const time_zone& z)Produces an output that is probably more meaningful to me than it is to you. I found it useful for debugging this library.
zoned_time
zoned_timerepresents a logical paring oftime_zoneand atime_pointwith precisionDuration. Ifsecondsis not implicitly convertible toDuration, the instantiation is ill-formed. [Note: There existtime_zones with UTC offsets that require a precision ofseconds. — end note:]template <class Duration> class zoned_time { const time_zone* zone_; // exposition only sys_time<Duration> tp_; // exposition only public: zoned_time(const zoned_time&) = default; zoned_time& operator=(const zoned_time&) = default; zoned_time(sys_time<Duration> st); explicit zoned_time(const time_zone* z); explicit zoned_time(const std::string& name); template <class Duration2, class = std::enable_if_t < std::is_convertible<sys_time<Duration2>, sys_time<Duration>>{} >> zoned_time(const zoned_time<Duration2>& zt) noexcept; zoned_time(const time_zone* z, local_time<Duration> tp); zoned_time(const std::string& name, local_time<Duration> tp); zoned_time(const time_zone* z, local_time<Duration> tp, choose c); zoned_time(const std::string& name, local_time<Duration> tp, choose c); zoned_time(const time_zone* z, const zoned_time<Duration>& zt); zoned_time(const std::string& name, const zoned_time<Duration>& zt); zoned_time(const time_zone* z, const zoned_time<Duration>& zt, choose); zoned_time(const std::string& name, const zoned_time<Duration>& zt, choose); zoned_time(const time_zone* z, const sys_time<Duration>& st); zoned_time(const std::string& name, const sys_time<Duration>& st); zoned_time& operator=(sys_time<Duration> st); zoned_time& operator=(local_time<Duration> ut); operator sys_time<Duration>() const; explicit operator local_time<Duration>() const; const time_zone* get_time_zone() const; local_time<Duration> get_local_time() const; sys_time<Duration> get_sys_time() const; sys_info get_info() const; }; using zoned_seconds = zoned_time<std::chrono::seconds>; template <class Duration1, class Duration2> bool operator==(const zoned_time<Duration1>& x, const zoned_time<Duration2>& y); template <class Duration1, class Duration2> bool operator!=(const zoned_time<Duration1>& x, const zoned_time<Duration2>& y);An invariant of
zoned_time<Duration>is that it always refers to a validtime_zone, and represents a point in time that exists and is not ambiguous.zoned_time<Duration>::zoned_time(const zoned_time&) = default; zoned_time<Duration>& zoned_time<Duration>::operator=(const zoned_time&) = default;The copy members transfer the associated
time_zonefrom the source to the destination. After copying, source and destination compare equal. IfDurationhasnoexceptcopy members, thenzoned_time<Duration>hasnoexceptcopy members.zoned_time<Duration>::zoned_time(sys_time<Duration> st);Effects: Constructs a
zoned_timeztsuch thatzt.get_time_zone()->name() == "UTC", andzt.get_sys_time() == st.explicit zoned_time<Duration>::zoned_time(const time_zone* z);Requires:
zrefers to a validtime_zone.Effects: Constructs a
zoned_timeztsuch thatzt.get_time_zone()-> == z, andzt.get_sys_time() == sys_seconds{}.explicit zoned_time<Duration>::zoned_time(const std::string& name);Effects: Equivalent to construction with
locate_zone(name).Throws: Any exception propagating out of
locate_zone(name).template <class Duration2, class = std::enable_if_t < std::is_convertible<sys_time<Duration2>, sys_time<Duration>>{} >> zoned_time<Duration>::zoned_time(const zoned_time<Duration2>& y) noexcept;Effects: Constructs a
zoned_timexsuch thatx == y.zoned_time<Duration>::zoned_time(const time_zone* z, local_time<Duration> tp);Requires:
zrefers to a validtime_zone.Effects: Constructs a
zoned_timeztsuch thatzt.get_time_zone()-> == z, andzt.get_local_time() == tp.Throws: Any exception that
z->to_sys(tp)would throw.zoned_time<Duration>::zoned_time(const std::string& name, local_time<Duration> tp);Effects: Equivalent to construction with
{locate_zone(name), tp}.zoned_time<Duration>::zoned_time(const time_zone* z, local_time<Duration> tp, choose c);Requires:
zrefers to a validtime_zone.Effects: Constructs a
zoned_timeztsuch thatzt.get_time_zone()-> == z, andzt.get_sys_time() == z->to_sys(tp, c).zoned_time<Duration>::zoned_time(const std::string& name, local_time<Duration> tp, choose c);Effects: Equivalent to construction with
{locate_zone(name), tp, c}.zoned_time<Duration>::zoned_time(const time_zone* z, const zoned_time<Duration>& y);Requires:
zrefers to a validtime_zone.Effects: Constructs a
zoned_timeztsuch thatzt.get_time_zone()-> == z, andzt.get_sys_time() == y.get_sys_time().zoned_time<Duration>::zoned_time(const std::string& name, const zoned_time<Duration>& y);Effects: Equivalent to construction with
{locate_zone(name), y}.zoned_time<Duration>::zoned_time(const time_zone* z, const zoned_time<Duration>& y, choose);Requires:
zrefers to a validtime_zone.Effects: Constructs a
zoned_timeztsuch thatzt.get_time_zone()-> == z, andzt.get_sys_time() == y.get_sys_time().Note: The
chooseparameter is allowed here, but has no impact.zoned_time<Duration>::zoned_time(const std::string& name, const zoned_time<Duration>& y, choose);Effects: Equivalent to construction with
{locate_zone(name), y}.Note: The
chooseparameter is allowed here, but has no impact.zoned_time<Duration>::zoned_time(const time_zone* z, const sys_time<Duration>& st);Requires:
zrefers to a validtime_zone.Effects: Constructs a
zoned_timeztsuch thatzt.get_time_zone()-> == z, andzt.get_sys_time() == st.zoned_time<Duration>::zoned_time(const std::string& name, const sys_time<Duration>& st);Effects: Equivalent to construction with
{locate_zone(name), st}.zoned_time<Duration>& zoned_time<Duration>::operator=(sys_time<Duration> st);Effects: After assignment
get_sys_time() == st. This assignment has no effect on the return value ofget_time_zone().Returns:
*this.zoned_time<Duration>& zoned_time<Duration>::operator=(local_time<Duration> lt);Effects: After assignment
get_local_time() == lt. This assignment has no effect on the return value ofget_time_zone().Returns:
*this.zoned_time<Duration>::operator sys_time<Duration>() const;Returns:
get_sys_time().explicit zoned_time<Duration>::operator local_time<Duration>() const;Returns:
get_local_time().const time_zone* zoned_time<Duration>::get_time_zone() const;Returns:
zone_.local_time<Duration> zoned_time<Duration>::get_local_time() const;Returns:
zone_->to_local(tp_).sys_time<Duration> zoned_time<Duration>::get_sys_time() const;Returns:
tp_.sys_info zoned_time<Duration>::get_info() const;Returns:
zone_->get_info(tp_).template <class Duration1, class Duration2> bool operator==(const zoned_time<Duration1>& x, const zoned_time<Duration2>& y);Returns:
x.zone_ == y.zone_ && x.tp_ == y.tp_.template <class Duration1, class Duration2> bool operator!=(const zoned_time<Duration1>& x, const zoned_time<Duration2>& y);Returns:
!(x == y).template <class CharT, class Traits, class Duration> std::basic_ostream<class CharT, class Traits>& operator<<(std::basic_ostream<class CharT, class Traits>& os, const zoned_time<Duration>& t)Effects: Streams
ttoosusing the format "%F %T %Z" and the value returned fromt.get_local_time().Returns:
os.
make_zonedThere exist several overloaded functions named
make_zonedwhich serve as factory functions forzoned_time<Duration>and will deduce the correctDurationfrom the argument list. In every case the correct return type iszoned_time<std::common_type_t<Duration, std::chrono::seconds>>.template <class Duration> zoned_time<std::common_type_t<Duration, std::chrono::seconds>> make_zoned(sys_time<Duration> tp)Returns:
{tp}.template <class Duration> zoned_time<std::common_type_t<Duration, std::chrono::seconds>> make_zoned(const time_zone* zone, local_time<Duration> tp)Returns:
{zone, tp}.template <class Duration> zoned_time<std::common_type_t<Duration, std::chrono::seconds>> make_zoned(const std::string& name, local_time<Duration> tp)Returns:
{name, tp}.template <class Duration> zoned_time<std::common_type_t<Duration, std::chrono::seconds>> make_zoned(const time_zone* zone, local_time<Duration> tp, choose c)Returns:
{zone, tp, c}.template <class Duration> zoned_time<std::common_type_t<Duration, std::chrono::seconds>> make_zoned(const std::string& name, local_time<Duration> tp, choose c)Returns:
{name, tp, c}.template <class Duration> zoned_time<std::common_type_t<Duration, std::chrono::seconds>> make_zoned(const time_zone* zone, const zoned_time<Duration>& zt)Returns:
{zone, zt}.template <class Duration> zoned_time<std::common_type_t<Duration, std::chrono::seconds>> make_zoned(const std::string& name, const zoned_time<Duration>& zt)Returns:
{name, zt}.template <class Duration> zoned_time<std::common_type_t<Duration, std::chrono::seconds>> make_zoned(const time_zone* zone, const zoned_time<Duration>& zt, choose c)Returns:
{zone, zt, c}.template <class Duration> zoned_time<std::common_type_t<Duration, std::chrono::seconds>> make_zoned(const std::string& name, const zoned_time<Duration>& zt, choose c)Returns:
{name, zt, c}.template <class Duration> zoned_time<std::common_type_t<Duration, std::chrono::seconds>> make_zoned(const time_zone* zone, const sys_time<Duration>& st)Returns:
{zone, st}.template <class Duration> zoned_time<std::common_type_t<Duration, std::chrono::seconds>> make_zoned(const std::string& name, const sys_time<Duration>& st)Returns:
{name, st}.
formattemplate <class CharT, class Traits, class Duration> std::basic_string<class CharT, class Traits> format(const std::locale& loc, std::basic_string<class CharT, class Traits> format, local_time<Duration> tp); template <class CharT, class Traits, class Duration> std::basic_string<class CharT, class Traits> format(std::basic_string<class CharT, class Traits> format, local_time<Duration> tp); template <class CharT, class Traits, class Duration> std::basic_string<class CharT, class Traits> format(const std::locale& loc, std::basic_string<class CharT, class Traits> format, const zoned_time<Duration>& tp); template <class CharT, class Traits, class Duration> std::basic_string<class CharT, class Traits> format(std::basic_string<class CharT, class Traits> format, const zoned_time<Duration>& tp); template <class CharT, class Traits, class Duration> std::basic_string<class CharT, class Traits> format(const std::locale& loc, std::basic_string<class CharT, class Traits> format, sys_time<Duration> tp); template <class CharT, class Traits, class Duration> std::basic_string<class CharT, class Traits> format(std::basic_string<class CharT, class Traits> format, sys_time<Duration> tp); // const CharT* formats template <class CharT, class Duration> std::basic_string<class CharT> format(const std::locale& loc, const CharT* format, local_time<Duration> tp); template <class CharT, class Duration> std::basic_string<class CharT> format(const CharT* format, local_time<Duration> tp); template <class CharT, class Duration> std::basic_string<class CharT> format(const std::locale& loc, const CharT* format, const zoned_time<Duration>& tp); template <class CharT, class Duration> std::basic_string<class CharT> format(const CharT* format, const zoned_time<Duration>& tp); template <class CharT, class Duration> std::basic_string<class CharT> format(const std::locale& loc, const CharT* format, sys_time<Duration> tp); template <class CharT, class Duration> std::basic_string<class CharT> format(const CharT* format, sys_time<Duration> tp);Effects: These functions create a formatted time stamp using the arguments, returning the result in a
std::string.If a
localeis passed in, then thatlocaleis used for any formatting that requires alocale. If nolocaleis passed in, then if alocaleis required for formatting, a default constructedlocalewill be used (which makes a copy of the globallocale).The
formatstring follows the rules as specified forstd::time_putwith the following exceptions:
If
%Sor%Tappears in theformatstring and the argumenttphas precision finer than seconds, then seconds are formatted as a decimal floating point number with a fixed format and a precision matching that of the precision oftp. The character for the decimal point is localized according to thelocale.If
%zappears in the format, the behavior depends on the type oftp:
local_time: An exception of typestd::runtime_erroris thrown.zoned_time: The offset associated withtp.get_time_zone()is used.sys_time:"+0000"is used.If
%Zappears in the format, the behavior depends on the type oftp:
local_time: An exception of typestd::runtime_erroris thrown.zoned_time: The abbreviation associated withtp.get_time_zone()is used.sys_time:"UTC"is used.For the overloads taking a
zoned_timeit is the value returned bytz.get_local_time()that is formatted.Returns: The formatted string.
parseOne can parse in a
sys_time<Duration>or alocal_time<Duration>. Optionally, one can also pass in a reference to astd::stringin order to capture the time zone abbreviation, or one can pass in a reference to astd::chrono::minutesto capture a time zone UTC offset (formatted as+0000), or one can pass in both in either order.template <class CharT, class Traits, class Duration> void parse(std::basic_istream<CharT, Traits>& is, const std::basic_string<CharT, Traits>& format, sys_time<Duration>& tp); template <class CharT, class Traits, class Duration> void parse(std::basic_istream<CharT, Traits>& is, const std::basic_string<CharT, Traits>& format, sys_time<Duration>& tp, std::basic_string<CharT, Traits>& abbrev); template <class CharT, class Traits, class Duration> void parse(std::basic_istream<CharT, Traits>& is, const std::basic_string<CharT, Traits>& format, sys_time<Duration>& tp, std::chrono::minutes& offset); template <class CharT, class Traits, class Duration> void parse(std::basic_istream<CharT, Traits>& is, const std::basic_string<CharT, Traits>& format, sys_time<Duration>& tp, std::basic_string<CharT, Traits>& abbrev, std::chrono::minutes& offset); template <class CharT, class Traits, class Duration> void parse(std::basic_istream<CharT, Traits>& is, const std::basic_string<CharT, Traits>& format, sys_time<Duration>& tp, std::chrono::minutes& offset, std::basic_string<CharT, Traits>& abbrev); template <class CharT, class Traits, class Duration> void parse(std::basic_istream<CharT, Traits>& is, const std::basic_string<CharT, Traits>& format, local_time<Duration>& tp); template <class CharT, class Traits, class Duration> void parse(std::basic_istream<CharT, Traits>& is, const std::basic_string<CharT, Traits>& format, local_time<Duration>& tp, std::basic_string<CharT, Traits>& abbrev); template <class CharT, class Traits, class Duration> void parse(std::basic_istream<CharT, Traits>& is, const std::basic_string<CharT, Traits>& format, local_time<Duration>& tp, std::chrono::minutes& offset); template <class CharT, class Traits, class Duration> void parse(std::basic_istream<CharT, Traits>& is, const std::basic_string<CharT, Traits>& format, local_time<Duration>& tp, std::basic_string<CharT, Traits>& abbrev, std::chrono::minutes& offset); template <class CharT, class Traits, class Duration> void parse(std::basic_istream<CharT, Traits>& is, const std::basic_string<CharT, Traits>& format, local_time<Duration>& tp, std::chrono::minutes& offset, std::basic_string<CharT, Traits>& abbrev); // const CharT* formats template <class CharT, class Traits, class Duration> void parse(std::basic_istream<CharT, Traits>& is, const CharT* format, sys_time<Duration>& tp); template <class CharT, class Traits, class Duration> void parse(std::basic_istream<CharT, Traits>& is, const CharT* format, sys_time<Duration>& tp, std::basic_string<CharT, Traits>& abbrev); template <class CharT, class Traits, class Duration> void parse(std::basic_istream<CharT, Traits>& is, const CharT* format, sys_time<Duration>& tp, std::chrono::minutes& offset); template <class CharT, class Traits, class Duration> void parse(std::basic_istream<CharT, Traits>& is, const CharT* format, sys_time<Duration>& tp, std::basic_string<CharT, Traits>& abbrev, std::chrono::minutes& offset); template <class CharT, class Traits, class Duration> void parse(std::basic_istream<CharT, Traits>& is, const CharT* format, sys_time<Duration>& tp, std::chrono::minutes& offset, std::basic_string<CharT, Traits>& abbrev); template <class CharT, class Traits, class Duration> void parse(std::basic_istream<CharT, Traits>& is, const CharT* format, local_time<Duration>& tp); template <class CharT, class Traits, class Duration> void parse(std::basic_istream<CharT, Traits>& is, const CharT* format, local_time<Duration>& tp, std::basic_string<CharT, Traits>& abbrev); template <class CharT, class Traits, class Duration> void parse(std::basic_istream<CharT, Traits>& is, const CharT* format, local_time<Duration>& tp, std::chrono::minutes& offset); template <class CharT, class Traits, class Duration> void parse(std::basic_istream<CharT, Traits>& is, const CharT* format, local_time<Duration>& tp, std::basic_string<CharT, Traits>& abbrev, std::chrono::minutes& offset); template <class CharT, class Traits, class Duration> void parse(std::basic_istream<CharT, Traits>& is, const CharT* format, local_time<Duration>& tp, std::chrono::minutes& offset, std::basic_string<CharT, Traits>& abbrev);Effects: These functions attempt to parse a
time_pointout ofisaccording toformat. If the parse is unsuccessful, callsis.setstate(std::ios::failbit)which may throw an exception.tp,abbrev, andoffsetare altered only in the event of a successful parse.The
formatstring follows the rules as specified forstd::time_getwith the following exceptions:
If
%Sor%Tappears in theformatstring and the argumenttphas precision finer than seconds, then the seconds are parsed as adouble, and if that parse is successful contributes to the time stamp as ifround<Duration>(duration<double>{s})wheresis a local variable holding the parseddouble.If
%zappears in theformatstring and an offset is successfully parsed, the overloads takingsys_timeinterprets the parsed time as a local time and subtracts the offset prior to assigning the value totp, resulting in a value oftprepresenting a UTC timestamp. The overloads takinglocal_timerequire a valid parse of the offset, but then ignore the offset in assigning a value to thelocal_time<Duration>& tp. Ifoffsetis passed in, on successful parse it will hold the value represented by%zif present, or will be assigned0minif%zis not present.If
%Zappears in theformatstring then an abbreviation is required in that position for a successful parse. The abbreviation will be parsed as astd::string(delimited by white space). The parsed abbreviation does not have to be a valid time zone abbreviation, and has no impact on the value parsed intotp. Using the overloads that take astd::string&one can discover what that parsed abbreviation is. On successful parse,abbrevwill be assigned the value represented by%Zif present, or assigned the empty string if%Zis not present.Note: There is no unique mapping from a time zone abbreviation to a
time_zone. But given a time zone abbreviation and asys_timeorlocal_time, one could make a list of potentialtime_zones. Given a UTC offset, one might even narrow that list down further.
utc_clockclass utc_clock { public: using duration = std::chrono::system_clock::duration; using rep = duration::rep; using period = duration::period; using time_point = std::chrono::time_point<utc_clock>; static constexpr bool is_steady = true; static time_point now() noexcept; template <class Duration> static utc_time<std::common_type_t<Duration, std::chrono::seconds>> sys_to_utc(sys_time<Duration> t); template <class Duration> static sys_time<std::common_type_t<Duration, std::chrono::seconds>> utc_to_sys(utc_time<Duration> u); }; template <class Duration> using utc_time = std::chrono::time_point<utc_clock, Duration>; using utc_seconds = utc_time<std::chrono::seconds>;In contrast to
sys_timewhich does not take leap seconds into account,utc_clockand its associatedtime_point,utc_time, counts time, including leap seconds, since 1970-01-01 00:00:00 UTC. It also provides functions for converting betweenutc_timeandsys_time. These functions consultget_tzdb().leapsto decide how many seconds to add/subtract in performing those conversions.static utc_clock::time_point utc_clock::now() noexcept;Returns:
sys_to_utc(system_clock::now()).template <class Duration> static utc_time<std::common_type_t<Duration, std::chrono::seconds>> utc_clock::sys_to_utc(sys_time<Duration> t);Returns: A
utc_timeu, such thatu.time_since_epoch() - t.time_since_epoch()is equal to the number of leap seconds that were inserted betweentand 1970-01-01. Iftis ambiguous on this issue (i.e. corresponds to the date of leap second insertion), then the conversion counts that leap second as inserted.template <class Duration> static sys_time<std::common_type_t<Duration, std::chrono::seconds>> utc_clock::utc_to_sys(utc_time<Duration> u);Returns: A
sys_timet, such thatutc_clock::sys_to_utc(t) == u.template <class Duration> utc_time<std::common_type_t<Duration, std::chrono::seconds>> to_utc_time(sys_time<Duration> t)Returns:
utc_clock::sys_to_utc(t).template <class Duration> sys_time<std::common_type_t<Duration, std::chrono::seconds>> to_sys_time(utc_time<Duration> u)Returns:
utc_clock::utc_to_sys(u).[Example:
#include "tz.h" #include <iostream> int main() { using namespace date; using namespace std::chrono_literals; auto t0 = sys_days{1972_y/jul/1} - 1ms; auto u0 = to_utc_time(t0); auto t1 = to_sys_time(u0); std::cout << t0 << ":\n"; std::cout << (u0.time_since_epoch() - t0.time_since_epoch()).count() << "ms\n"; std::cout << (t1 - t0).count() << "ms\n\n"; t0 += 1ms; u0 = to_utc_time(t0); t1 = to_sys_time(u0); std::cout << t0 << ":\n"; std::cout << (u0.time_since_epoch() - t0.time_since_epoch()).count() << "ms\n"; std::cout << (t1 - t0).count() << "ms\n"; }Output:
1972-06-30 23:59:59.999: 0ms 0ms 1972-07-01 00:00:00.000: 1000ms 0ms— end example]
leapclass leap { public: leap(const leap&) = default; leap& operator=(const leap&) = default; // Undocumented constructors sys_seconds date() const; }; bool operator==(const leap& x, const leap& y); bool operator!=(const leap& x, const leap& y); bool operator< (const leap& x, const leap& y); bool operator> (const leap& x, const leap& y); bool operator<=(const leap& x, const leap& y); bool operator>=(const leap& x, const leap& y); template <class Duration> bool operator==(const const leap& x, const sys_time<Duration>& y); template <class Duration> bool operator==(const sys_time<Duration>& x, const leap& y); template <class Duration> bool operator!=(const leap& x, const sys_time<Duration>& y); template <class Duration> bool operator!=(const sys_time<Duration>& x, const leap& y); template <class Duration> bool operator< (const leap& x, const sys_time<Duration>& y); template <class Duration> bool operator< (const sys_time<Duration>& x, const leap& y); template <class Duration> bool operator> (const leap& x, const sys_time<Duration>& y); template <class Duration> bool operator> (const sys_time<Duration>& x, const leap& y); template <class Duration> bool operator<=(const leap& x, const sys_time<Duration>& y); template <class Duration> bool operator<=(const sys_time<Duration>& x, const leap& y); template <class Duration> bool operator>=(const leap& x, const sys_time<Duration>& y); template <class Duration> bool operator>=(const sys_time<Duration>& x, const leap& y);
leapis a copyable class that is constructed and stored in the time zone database when initialized. You can explicitly convert it to asys_secondswith the member functiondate()and that will be the date of the leap second insertion.leapis equality and less-than comparable, both with itself, and withsys_time<Duration>.
linkclass link { public: link(const link&) = default; link& operator=(const link&) = default; // Undocumented constructors const std::string& name() const; const std::string& target() const; }; bool operator==(const link& x, const link& y); bool operator!=(const link& x, const link& y); bool operator< (const link& x, const link& y); bool operator> (const link& x, const link& y); bool operator<=(const link& x, const link& y); bool operator>=(const link& x, const link& y);A
linkis an alternative name for atime_zone. The alternative name isname(). The name of thetime_zonefor which this is an alternative name istarget().links will be constructed for you when the time zone database is initialized.
You will need the following four source files:
date.h,
tz.h,
tz_private.h and
tz.cpp.
These sources are located at the github repository
https://github.com/HowardHinnant/date.
The source
tz.cpp
contains the following string near the top:
static std::string install{"~/Downloads/tzdata"}; // "c:\\tzdata" on Windows
You should set this such that install points to the directory
where your library or application can find the downloaded and uncompressed
IANA Time Zone Database (or where
you want the software to install it for you if you compile with
AUTO_DOWNLOAD == 1).
There are three configuration macros that can be defined on the command line during compilation, or you can ignore them and they will take on default values.
HAS_REMOTE_APIDefaults to 1 on Linux and OS X, and to 0 on Windows AUTO_DOWNLOADDefaults to HAS_REMOTE_APILAZY_INITDefaults to 1
If HAS_REMOTE_API is 1 then the remote API exists,
else it doesn't:
std::string remote_version(); bool remote_download(const std::string& version); bool remote_install(const std::string& version);
The remote API requires linking against libcurl
(https://curl.haxx.se/libcurl).
On OS X and Linux this is done with -lcurl.
libcurl comes pre-installed on OS X and Linux, but not on Windows.
However one can download it for Windows.
If AUTO_DOWNLOAD is 1 then first access to the timezone database will install
it if it hasn't been installed, and if it has, will use the remote API to install the
latest version if not already installed.
If LAZY_INIT is on, the Zones are not fully compiled upon first
access to the database. As each Zone is accessed individaully by the
programmer (when they are used), they are fully compiled at that point. However, this
further Zone compilation does not involve any access to the local copy of the
tz database files.
If LAZY_INIT is off, every Zone is fully compiled upon first
access to the database.
LAZY_INIT speeds up the initialization of the database, but slows down the
first use of any individual Zone. If you are only using a few
Zones then LAZY_INIT is a clear win. If you are immediately
using all of the Zones (say for some database analysis) then
LAZY_INIT is not a win.
If LAZY_INIT is off, and you are on multi-core hardware, and your application
has other unrelated initialization it has to take care of, spinning off timezone
initialization into a detached thread can be an attractive option:
int
main()
{
std::thread(date::get_tzdb).detach();
// other initialization ...
}
By the time your application actually needs to use the timezone database, it is likely to be fully initialized and ready to go. And if it is not, C++11 threadsafe function local statics ensure there is no race condition on the initialization.
If you would like to trade off functionality for size, you can reduce the size of the database in two ways:
You can limit geography by removing one or more of the files in this list:
const std::vector<const std::string> files =
{
"africa", "antarctica", "asia", "australasia", "backward", "etcetera", "europe",
"pacificnew", "northamerica", "southamerica", "systemv", "leapseconds"
};
You can limit history by setting min_year to something more recent such as:
CONSTDATA auto min_year = 2015_y;
When you do so, if you ask to convert a date prior to min_year, an exception
will be thrown.
The entire database consumes about 859Kb.
Compile
tz.cpp
in with the rest of your library or application.
If AUTO_DOWNLOAD is not enabled, you are responsible for keeping your
IANA Time Zone Database up to date. New
versions of it are released several times a year. This library is not bundled with a
specific version of the database already installed, nor is any specific version of the
database blessed.
There is no preprocessing of the IANA Time Zone Database required. This library efficiently initializes itself directly from the files of the IANA Time Zone Database.
A database parser is nothing without its database. I would like to thank the founding contributor of the IANA Time Zone Database Arthur David Olson. I would also like to thank the entire group of people who continually maintain it, and especially the IESG-designated TZ Coordinator, Paul Eggert. Without the work of these people, this software would have no data to parse.
I would also like to thank Jiangang Zhuang and Bjarne Stroustrup for invaluable feedback for the timezone portion of this library, which ended up also influencing the date.h library.
And I would also especially like to thank contributors to this library: gmcode, Ivan Pizhenko, tomy2105 and Ville Voutilainen.