diff --git a/date.html b/date.html index c8c60a2..03ac2bb 100644 --- a/date.html +++ b/date.html @@ -26,7 +26,7 @@

Howard E. Hinnant
-2016-05-15
+2016-05-20
Creative
 Commons License
This work is licensed @@ -1314,7 +1314,6 @@ Here is a function which will discover the limits for a single durration D #include "date.h" #include <iomanip> #include <iostream> -#include <limits> #include <cstdint> template <class D> @@ -1325,8 +1324,8 @@ limit(const std::string& msg) using namespace std; using namespace std::chrono; using dsecs = sys_time<duration<double>>; - constexpr auto ymin = sys_days{year{numeric_limits<int16_t>::min()}/jan/1}; - constexpr auto ymax = sys_days{year{numeric_limits<int16_t>::max()}/12/last}; + constexpr auto ymin = sys_days{year::min()}/jan/1}; + constexpr auto ymax = sys_days{year::max()}/12/last}; constexpr auto dmin = sys_time<D>::min(); constexpr auto dmax = sys_time<D>::max(); cout << left << setw(24) << msg << " : ["; @@ -2623,7 +2622,7 @@ constexpr bool year::ok() const noexcept;

-Returns: min() <= *this && *this <= max(). +Returns: true.

@@ -2633,12 +2632,7 @@ static constexpr year year::min() noexcept;

-Returns: A year constructed with the minimum representable year -number. This year shall be a value such that -sys_days(min()/jan/1) + Unit{0}, where Unit is one of -microseconds, milliseconds, seconds, -minutes, or hours, there shall be no overflow. [Note: -nanoseconds is intentionally omitted from this list. — end note] +Returns: year{std::numeric_limits<short>::min()}.

@@ -2648,12 +2642,7 @@ static constexpr year year::max() noexcept;

-Returns: A year constructed with the maximum representable year -number. This year shall be a value such that -sys_days(max()/dec/31) + Unit{0}, where Unit is one of -microseconds, milliseconds, seconds, -minutes, or hours, there shall be no overflow. [Note: -nanoseconds is intentionally omitted from this list. — end note] +Returns: year{std::numeric_limits<short>::max()}.

diff --git a/iso_week.html b/iso_week.html index 4e5a812..82c0dff 100644 --- a/iso_week.html +++ b/iso_week.html @@ -26,7 +26,7 @@

Howard E. Hinnant
-2015-12-23
+2016-05-21
Creative
 Commons License
This work is licensed @@ -51,7 +51,7 @@ Commons Attribution 4.0 International License. This paper fully documents an ISO week date calendar that is fully interoperable with -date. +date.

Implementation

@@ -101,8 +101,8 @@ and then print them back out when requested.

The real power of year_weeknum_weekday is that it can implicitly convert to -and from day_point. And - day_point is just a type alias for: +and from sys_days. And + sys_days is just a type alias for:

@@ -110,11 +110,11 @@ std::chrono::time_point<std::chrono::system_clock, days>
 

-This is the exact same day_point used by the -date and time zone -libraries. And so by simply having conversions to and from day_point, +This is the exact same sys_days used by the +date and time zone +libraries. And so by simply having conversions to and from sys_days, iso_week::year_weeknum_weekday becomes seamlessly interoperable with all -of the types in date and +of the types in date and time zone such as date::year_month_day and date::Zone:

@@ -194,7 +194,7 @@ In particular, note that for this day, the iso_week::year is 2015 a This brings us to an important type-safety feature of these libraries: The iso_week::year and the date::year are two distinct types. They represent concepts that are very similar. They almost always have the same -value for the same day_point. But as in this example, they are occasionally +value for the same sys_days. But as in this example, they are occasionally different. It is not unheard of for computer systems to conflate these two very similar types, resulting in bugs that are hard to find because they are relatively rare. Having the C++ type system help you keep these similar but different concepts distinct is a @@ -219,7 +219,7 @@ and predictable.    time_point  - day_pointsys_days    types  @@ -289,7 +289,7 @@ etc.) are in namespace iso_week::literals and imported into namespa This definition is not an SI unit but is accepted for use with SI. days is the resultant type when subtracting two -day_points. +sys_dayss.

 using days = std::chrono::duration
@@ -325,20 +325,20 @@ using years = std::chrono::duration
     <int, std::ratio_multiply<std::ratio<146097, 400>, days::period>>;
 
-

day_point

+

sys_days

-day_point is a std::chrono::time_point using +sys_days is a std::chrono::time_point using std::chrono::system_clock and days. This makes -day_point interoperable with +sys_days interoperable with std::chrono::system_clock::time_point. It is simply a count of days since the epoch of std::chrono::system_clock which in every implementation is -Jan. 1, 1970. day_point is a serial-based time point with a resolution of +Jan. 1, 1970. sys_days is a serial-based time point with a resolution of days.

-using day_point = std::chrono::time_point<std::chrono::system_clock, days>;
+using sys_days = std::chrono::time_point<std::chrono::system_clock, days>;
 
@@ -389,7 +389,7 @@ class weekday unsigned char wd_; // exposition only public: explicit constexpr weekday(unsigned wd) noexcept; - constexpr weekday(const day_point& dp) noexcept; + constexpr weekday(const sys_days& dp) noexcept; constexpr weekday(date::weekday wd) noexcept; explicit weekday(int) = delete; @@ -447,7 +447,7 @@ day of the week.

-A weekday can be implicitly constructed from a day_point. This +A weekday can be implicitly constructed from a sys_days. This is the computation that discovers the day of the week of an arbitrary date.

@@ -481,13 +481,13 @@ explicit constexpr weekday::weekday(unsigned wd) noexcept;
-constexpr weekday(const day_point& dp) noexcept;
+constexpr weekday(const sys_days& dp) noexcept;
 

Effects: Constructs an object of type weekday by computing what day -of the week corresponds to the day_point dp, and representing that day of +of the week corresponds to the sys_days dp, and representing that day of the week in wd_.

@@ -1221,7 +1221,7 @@ static constexpr year year::min() noexcept;

Returns: A year constructed with the minimum representable year number. This year shall be a value such that -day_point{min()/1_w/mon} + Unit{0}, where Unit is one of +sys_days{min()/1_w/mon} + Unit{0}, where Unit is one of microseconds, milliseconds, seconds, minutes, or hours, there shall be no overflow. [Note: nanoseconds is intentionally omitted from this list. — end note] @@ -1236,7 +1236,7 @@ static constexpr year year::max() noexcept;

Returns: A year constructed with the maximum representable year number. This year shall be a value such that -day_point{max()/last/sun} + Unit{0}, where Unit is one of +sys_days{max()/last/sun} + Unit{0}, where Unit is one of microseconds, milliseconds, seconds, minutes, or hours, there shall be no overflow. [Note: nanoseconds is intentionally omitted from this list. — end note] @@ -2163,7 +2163,7 @@ public: constexpr year_weeknum_weekday(const iso_week::year& y, const iso_week::weeknum& wn, const iso_week::weekday& wd) noexcept; constexpr year_weeknum_weekday(const year_lastweek_weekday& x) noexcept; - constexpr year_weeknum_weekday(const day_point& dp) noexcept; + constexpr year_weeknum_weekday(const sys_days& dp) noexcept; year_weeknum_weekday& operator+=(const years& y) noexcept; year_weeknum_weekday& operator-=(const years& y) noexcept; @@ -2172,7 +2172,7 @@ public: constexpr iso_week::weeknum weeknum() const noexcept; constexpr iso_week::weekday weekday() const noexcept; - constexpr operator day_point() const noexcept; + constexpr operator sys_days() const noexcept; constexpr bool ok() const noexcept; }; @@ -2197,7 +2197,7 @@ std::ostream& operator<<(std::ostream& os, const year_weeknum_week and weekday in the ISO week date calendar. One can observe each field. year_weeknum_weekday supports year-oriented -arithmetic. There is an implicit conversion to and from day_point. There is +arithmetic. There is an implicit conversion to and from sys_days. There is also an implicit conversion from year_lastweek_weekday. year_weeknum_weekday is equality and less-than comparable.

@@ -2237,7 +2237,7 @@ constructing y_ with x.year(), wn_ with
-constexpr year_weeknum_weekday::year_weeknum_weekday(const day_point& dp) noexcept;
+constexpr year_weeknum_weekday::year_weeknum_weekday(const sys_days& dp) noexcept;
 
@@ -2248,7 +2248,7 @@ corresponds to the date represented by dp.

Remarks: For any value of year_weeknum_weekday, x, for which x.ok() is true, this equality will also be -true: x == year_weeknum_weekday{day_point{x}}. +true: x == year_weeknum_weekday{sys_days{x}}.

@@ -2309,12 +2309,12 @@ constexpr iso_week::weekday year_weeknum_weekday::weekday() const noexcept;
-constexpr year_weeknum_weekday::operator day_point() const noexcept;
+constexpr year_weeknum_weekday::operator sys_days() const noexcept;
 

-Returns: A day_point which represents the date represented by +Returns: A sys_days which represents the date represented by *this.

@@ -2464,7 +2464,7 @@ public: constexpr iso_week::weeknum weeknum() const noexcept; constexpr iso_week::weekday weekday() const noexcept; - constexpr operator day_point() const noexcept; + constexpr operator sys_days() const noexcept; constexpr bool ok() const noexcept; }; @@ -2489,7 +2489,7 @@ std::ostream& operator<<(std::ostream& os, const year_lastweek_wee and weekday in the last week of the ISO week date calendar. One can observe each field. year_lastweek_weekday supports year-oriented -arithmetic. There is an implicit conversion to day_point. +arithmetic. There is an implicit conversion to sys_days. year_lastweek_weekday is equality and less-than comparable.

@@ -2571,12 +2571,12 @@ constexpr iso_week::weekday year_lastweek_weekday::weekday() const noexcept;
-constexpr year_lastweek_weekday::operator day_point() const noexcept;
+constexpr year_lastweek_weekday::operator sys_days() const noexcept;
 

-Returns: A day_point which represents the date represented by +Returns: A sys_days which represents the date represented by *this.

diff --git a/tz.html b/tz.html index 44ad845..c632416 100644 --- a/tz.html +++ b/tz.html @@ -26,7 +26,7 @@

Howard E. Hinnant
-2016-05-15
+2016-05-20
Creative
 Commons License
This work is licensed @@ -43,19 +43,12 @@ Commons Attribution 4.0 International License.
  • Introduction
  • Description
  • Reference @@ -150,11 +143,12 @@ this namespace in example code below is intentionally omitted in the hopes of re verbosity.

    -

    What is the current local time?

    +

    What is the current local time?

    -One of the first things people want to is find out what current local time it is. -Here is a complete program to print out the local time in human readable format: +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:

    @@ -166,8 +160,8 @@ main()
     {
         using namespace date;
         using namespace std::chrono;
    -    auto local_time = make_zoned(current_zone(), system_clock::now());
    -    std::cout << local_time << '\n';
    +    auto t = make_zoned(current_zone(), system_clock::now());
    +    std::cout << t << '\n';
     }
     
    @@ -202,10 +196,10 @@ and pairing that with a system_clock::time_point using
  • This zoned_time maintains whatever precision it was given. On my platform system_clock::now() has microseconds precision, so in this -example, local_time has microseconds precision as well. +example, t has microseconds precision as well.

  • -The local_time is then simply streamed out. By default the output +Then t is simply streamed out. By default the output represents all of the precision it is given.

  • @@ -218,13 +212,13 @@ and the time zone. But by default, things just work, and don't throw away infor

    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 local_time have just a precision of milliseconds +makes t have just a precision of milliseconds and that is reflected in the streaming operator with no further effort:

    -auto local_time = make_zoned(current_zone(), floor<milliseconds>(system_clock::now()));
    -std::cout << local_time << '\n';  // 2016-05-14 18:33:24.205 EDT
    +auto t = make_zoned(current_zone(), floor<milliseconds>(system_clock::now()));
    +std::cout << t << '\n';  // 2016-05-14 18:33:24.205 EDT
     

    @@ -232,8 +226,8 @@ Seconds precision is just as easy:

    -auto local_time = make_zoned(current_zone(), floor<seconds>(system_clock::now()));
    -std::cout << local_time << '\n';  // 2016-05-14 18:33:24 EDT
    +auto t = make_zoned(current_zone(), floor<seconds>(system_clock::now()));
    +std::cout << t << '\n';  // 2016-05-14 18:33:24 EDT
     

    @@ -242,8 +236,8 @@ also at your fingertips (and at any precision):

    -auto local_time = make_zoned(current_zone(), system_clock::now());
    -std::cout << format("%a, %b %d, %Y at %I:%M %p %Z", local_time) << '\n';
    +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
     
    @@ -252,12 +246,12 @@ Using any std::locale your OS supports:

    -auto local_time = make_zoned(current_zone(), floor<seconds>(system_clock::now()));
    -std::cout << format(locale("de_DE"), "%a, %b %d, %Y at %T %Z", local_time) << '\n';
    +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
     
    -

    What time is it somewhere else in the world?

    +

    What time is it somewhere else in the world?

    From the previous section: @@ -275,8 +269,8 @@ We can fix that easily too:

     auto zone = locate_zone("Europe/Berlin");
    -auto local_time = make_zoned(zone, floor<seconds>(system_clock::now()));
    -std::cout << format(locale("de_DE"), "%a, %b %d, %Y at %T %Z", local_time) << '\n';
    +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
     
    @@ -293,7 +287,7 @@ You can also call make_zoned with the time zone name right in the c

    -auto local_time = make_zoned("Europe/Berlin", floor<seconds>(system_clock::now()));
    +auto t = make_zoned("Europe/Berlin", floor<seconds>(system_clock::now()));
     

    @@ -301,7 +295,7 @@ The first way is very slightly more efficient if you plan on using zone -

    How do I convert a time_zone from one time zone to another?

    +

    How do I convert a time_zone from one time zone to another?

    So far we've only looked at converting from system_clock::now() to @@ -347,6 +341,32 @@ 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 @@ -354,751 +374,187 @@ serve as a translator to any other time_point which carries time zo information (to any precision).

    - - - - - - - - - - - - - - - - - - - -

    The Database

    +

    local_time vs sys_time

    -The database is represented with the type TZ_DB: +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.

    -struct TZ_DB
    -{
    -    std::string       version;
    -    std::vector<Zone> zones;
    -    std::vector<Link> links;
    -    std::vector<Leap> leaps;
    -    std::vector<Rule> rules;
    -};
    +auto new_years = local_time<days>{2017_y/jan/1} + 0h + 0m + 0s;
     

    -This is a singleton class. You can get a const TZ_DB& to the singleton -using this function: +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_time is 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:

    -const TZ_DB& get_tzdb();
    +using local_seconds = local_time<std::chrono::seconds>;
    +using local_days    = local_time<days>;
     

    -The first call to get_tzdb() will initialize the database from your local -copy of the IANA Time Zone Database located -at install (a file-scope variable of type std::string in -tz.cpp). -You will need to catch the return of this function by const& as the -TZ_DB is not constructible from a const TZ_DB. This can be done -with the following example code: +In summary: When is 1min after New Years 2017?

    -auto& db = get_tzdb();
    +auto t = local_days{jan/1/2017} + 1min;
    +cout << t << '\n';  // 2017-01-01 00:01
     

    -With a reference to the database in hand, you have read-only access to the entire -database, which is nothing more than sorted vectors for the four types of -data contained in the database. With such a reference you could (for example) print the -names of all the Zones in the database: +When is 1min after New Years 2017 UTC?

    -for (auto& z : db.zones)
    -    std::cout << z.name() << '\n';
    +auto t = sys_days{jan/1/2017} + 1min;
    +cout << t << '\n';  // 2017-01-01 00:01
     

    -There are currently 377 zones in the database. +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.

    -Or you could output the 89 Links, including their name() and -target(): +When is 1min after New Years 2017 in New York?

    -for (auto& link : db.links)
    -    std::cout << link << '\n';
    +zoned_seconds t{"America/New_York", local_days{jan/1/2017} + 1min};
    +cout << t << '\n';  // 2017-01-01 00:01:00 EST
     

    -If you aren't happy with the format this outputs in, Link has public member -functions name() and target() so that you can achieve whatever -format you desire. -

    - -

    -If needed, db.version is a std::string containing the -IANA Time Zone Database version of the -database you are reading. For example the current version when this sentence -was written was "2016a". -

    - -

    -You can even print the entire database out in a semi-human-readable format if desired: +What time will it be in New York when it is 1min after New Years 2017 UTC?

    -std::cout << db << '\n';
    +zoned_seconds t{"America/New_York", sys_days{jan/1/2017} + 1min};
    +cout << t << '\n';  // 2016-12-31 19:01:00 EST
     
    -

    -If you constrain the geography or history of the database during installation, those -constraints will be reflected in these examples. -

    +

    Summary

    -If you decide you need to reload the database say, because you want to install a new -version of the IANA Time Zone Database -without stopping your program, you can use this function: -

    - -
    -const TZ_DB& reload_tzdb();
    -
    - -

    -This re-initializes the database by reading from the install location you -customized on installation. The use of the reload_tzdb function is not -pain-free, and not for every application (not for most of them I'm guessing). For example -see the Thread Safety section for issues related to the use of these functions. -

    - -

    The remote API

    - -

    -The remote API is enabled only if HAS_REMOTE_API is set to 1 during -compilation. See Installation for more details. -

    - -
    -std::string remote_version();
    -
    - -

    -This function will query the -IANA Time Zone Database website for the -latest version number of the IANA database, and return it as a std::string. -If an internet connection can not be made, an empty string is returned. -This string can be compared against the version of your local copy of the database: -get_tzdb().version. -

    - -
    -bool remote_download(const std::string& version);
    -
    - -

    -This function will attempt to download the database with the version version -from the IANA Time Zone Database website. -If successful, true is returned and a file named -version + ".tar.gz" will be stored at the location install. -If not successful, false is returned. -

    - -
    -bool remote_install(const std::string& version);
    -
    - -

    -This function will attempt to uncompress the tar file downloaded by -remote_download(version) and replace any existing database with -the result. It will then delete the tar file. If the tar file doesn't exist, -remote_install will do nothing. Returns true on -success, else returns false. -

    - -

    Zone

    - -

    -The Zone class is the most important type in this library. It provides the -main access to the functionality provided by this library. Each Zone is -named, represents a geographic area, and provides a mapping between UTC and the local -time, in both directions. This mapping from local time to UTC is in general not one to -one. The mapping, and even the specific rule, depends upon the input -time_point, which can represent either UTC or local time. -

    - -

    -The detailed API of the Zone class depends upon a small amount of -infrastructure which is introduced first. -

    - -

    Infrastructure

    - -
    -using second_point = std::chrono::time_point<std::chrono::system_clock,
    -                                             std::chrono::seconds>;
    -
    - -

    -second_point is a std::chrono::time_point based on -system_clock but with the precision of seconds. This library -will interoperate with system_clock::time_points of any precision. -However the data in the database is largely based on second_point, and -some of the data which is presented, such as that in the sys_info class, uses -this type alias as a convenience, and to reduce verbosity. second_point -will implicitly convert to system_clock::time_point. And coarser -time_points such as the day_point from the -date library will implicitly convert to -second_point. -

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

    -The sys_info struct is the return type of the get_info member -function of the Zone class. It contains very detailed information about the -Zone at the time_point (UTC or local) input into this member -function. sys_info contains no pointers or references into the database. -Therefore clients do not need to be concerned about holding on to sys_infos -during a call to reload_tzdb(). Though a call to reload_tzdb() -could potentially make the data in an outstanding sys_info obsolete. See -Zone::get_info for more details. -

    - -
    -enum class tz {utc, local};
    -enum class choose {earliest, latest};
    -
    - -

    -These enums are used as input to some of the Zone member -functions. tz::utc indicates that a time_point represents a -time in the UTC time zone. tz::local indicates that a -time_point represents a time in the Zone's local time zone. -The choose enum allows a client to specify how a mapping from local to UTC -should behave when the mapping is not one to one. Alternatively one can not specify -a policy in the mapping, and if the mapping is not unique, an exception will be thrown. -

    - -
    -class nonexistent_local_time
    -    : public std::runtime_error
    -{
    -public:
    -    const char* what() const override;
    -};
    -
    -class ambiguous_local_time
    -    : public std::runtime_error
    -{
    -public:
    -    const char* what() const override;
    -};
    -
    - -

    -These are the exception classes thrown by the local to UTC mapping. In addition to their -type indicating the nature of the exceptional circumstance, they also sport a -what() member function that will contain a very detailed explanation -including specific times for the specific time_points involved in the -attempted mapping. -

    - -

    -If in a call to Zone::to_sys the local time_point falls into a -"gap" for which no local time exists, a nonexistent_local_time exception is -thrown. -

    - -

    -If in a call to Zone::to_sys the local time_point has an -ambiguous mapping to UTC, a ambiguous_local_time exception is thrown. -

    - -

    -Either exceptional situation can be circumvented with the use of -choose::earliest or choose::latest in the call to -to_sys. -

    - -

    Zone continued

    - -
    -class Zone
    -{
    -public:
    -    const std::string& name() const;
    -
    -    template <class Rep, class Period>
    -    std::pair
    -    <
    -        std::chrono::time_point<std::chrono::system_clock,
    -            typename std::common_type<std::chrono::duration<Rep, Period>,
    -                                      std::chrono::seconds>::type>,
    -        std::string
    -    >
    -    to_local(std::chrono::time_point<std::chrono::system_clock,
    -                                     std::chrono::duration<Rep, Period>> tp) const;
    -
    -    template <class Rep, class Period>
    -    std::chrono::time_point<std::chrono::system_clock,
    -        typename std::common_type<std::chrono::duration<Rep, Period>,
    -                                  std::chrono::seconds>::type>
    -    to_sys(std::chrono::time_point<std::chrono::system_clock,
    -                                   std::chrono::duration<Rep, Period>> tp) const;
    -
    -    template <class Rep, class Period>
    -    std::chrono::time_point<std::chrono::system_clock,
    -        typename std::common_type<std::chrono::duration<Rep, Period>,
    -                                  std::chrono::seconds>::type>
    -    to_sys(std::chrono::time_point<std::chrono::system_clock,
    -                                   std::chrono::duration<Rep, Period>> tp,
    -           choose z) const;
    -
    -    template <class Rep, class Period>
    -    sys_info
    -    get_info(std::chrono::time_point<std::chrono::system_clock,
    -                                     std::chrono::duration<Rep, Period>> tp,
    -             tz timezone) const;
    -};
    -
    -const Zone* locate_zone(const std::string& tz_name);
    -const Zone* current_zone();
    -
    -bool operator==(const Zone& x, const Zone& y);
    -bool operator!=(const Zone& x, const Zone& y);
    -bool operator< (const Zone& x, const Zone& y);
    -bool operator> (const Zone& x, const Zone& y);
    -bool operator<=(const Zone& x, const Zone& y);
    -bool operator>=(const Zone& x, const Zone& y);
    -
    -std::ostream& operator<<(std::ostream& os, const Zone& z);
    -
    - -

    -The entire public API of the Zone is const. Once the database -is initialized (or reloaded), Zones are set in concrete. -

    - -
    - -

    -The current time zone associated with your computer can be retrieved with the namespace -scope function current_zone(). For example: -

    - -
    -std::cout << current_zone()->name() << '\n';
    -
    - -

    -For me the above currently outputs America/New_York. -

    - -
    - -
    -const Zone* locate_zone(const std::string& tz_name);
    -
    - -

    -locate_zone returns a pointer to a Zone in the database -associated with tz_name. If it can't find a Zone named -tz_name, the implementation will search for a Link named -tz_name, and then return the Zone associated with the -Link's target(). If tz_name can not be found in the -database, a std::runtime_error is thrown. -

    - -

    -Example: -

    - -
    -try
    -{
    -    cout << locate_zone("Europe/London")->name() << '\n';     // A Zone
    -    cout << locate_zone("Europe/Jersey")->name() << '\n';     // A Link to a Zone
    -    cout << locate_zone("Europe/New_Jersey")->name() << '\n'; // Doesn't exist
    -}
    -catch (const exception& e)
    -{
    -    cout << e.what() << '\n';
    -}
    -
    - -

    -Which outputs: -

    - -
    -Europe/London
    -Europe/London
    -Europe/New_Jersey not found in timezone database
    -
    - -

    -Note that locate_zone never returns nullptr. Also note that -the first call to locate_zone may implicitly initialize the database. -

    - -
    - -
    -template <class Rep, class Period>
    -std::pair
    -<
    -    std::chrono::time_point<std::chrono::system_clock,
    -        typename std::common_type<std::chrono::duration<Rep, Period>,
    -                                  std::chrono::seconds>::type>,
    -    std::string
    ->
    -to_local(std::chrono::time_point<std::chrono::system_clock,
    -                                 std::chrono::duration<Rep, Period>> tp) const;
    -
    - -

    -to_local maps a system_clock-associated time_point -from UTC to local time, returning both the mapped time_point and an -abbreviation for the local time zone. This member function accepts any precision -time_point, but returns a time_point with a precision of -seconds or finer. This is done because it is possible that some of the -mappings returned by the database need the precision of a second. -

    - -

    -There are only two ways this function can fail: +We now have 5 concepts and their associated types:

    1. -Out of memory error. Not bloody likely. The only memory that possibly could be -allocated is for the abbreviation stored in a std::string and all known -implementations will fit all known abbreviations into their short string buffer. +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.

    2. +
    3. -If you curtailed history during installation, a runtime_error will be thrown -if tp refers to a time_point outside of the range -min_year/jan/1 00:00:00 to max_year/dec/31 23:59:59. This can -not happen with the default settings of min_year and max_year. +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. +

    4. + +
    5. +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. +

    6. + +
    7. +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. +

    8. + + +
    9. +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.

    -Example: -

    - -
    -auto local = current_zone()->to_local(system_clock::now());
    -cout << local.first << ' ' << local.second << '\n';
    -
    - -

    -Which just output for me: -

    - -
    -2015-07-12 16:57:14.430467 EDT
    -
    - -

    -Not quite 5pm in the US Eastern timezone during daylight saving time. +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.

    -And for a historical example: +Full formatting and parsing facilities are available with strftime-like +formatting strings.

    -
    -auto distant_past = locate_zone("America/New_York")->to_local(day_point(feb/9/1942) + 7h);
    -cout << distant_past.first << ' ' << distant_past.second << '\n';
    -
    +

    Examples

    -Which outputs: -

    - -
    -1942-02-09 03:00:00 EWT
    -
    - -

    -The US shifted to "War Time." -

    - -
    - -

    -If you want to go the other direction (from local time to UTC) use: -

    - -
    -template <class Rep, class Period>
    -std::chrono::time_point<std::chrono::system_clock,
    -    typename std::common_type<std::chrono::duration<Rep, Period>,
    -                              std::chrono::seconds>::type>
    -to_sys(std::chrono::time_point<std::chrono::system_clock,
    -                               std::chrono::duration<Rep, Period>> tp) const;
    -
    - -

    -For example: -

    - -
    -auto distant_past = locate_zone("America/New_York")->to_sys(day_point(feb/9/1942) + 3h);
    -cout << distant_past << ' ' << " UTC\n";
    -
    - -

    -Which outputs: -

    - -
    -1942-02-09 07:00:00 UTC
    -
    - -

    -This function will throw an exception of type nonexistent_local_time if the -local time does not exist. This can happen when the local clock is discontinuously set -forward, such as when moving from standard time to daylight savings time. -

    - -

    -For example: -

    - -
    -try
    -{
    -    auto distant_past = locate_zone("America/New_York")->to_sys(day_point(feb/9/1942) + 3h - 1ms);
    -    cout << distant_past << ' ' << " UTC\n";
    -}
    -catch (const exception& e)
    -{
    -    cout << e.what() << '\n';
    -}
    -
    - -

    -Which outputs: -

    - -
    -1942-02-09 02:59:59.999 is in a gap between
    -1942-02-09 02:00:00 EST and
    -1942-02-09 03:00:00 EWT which are both equivalent to
    -1942-02-09 07:00:00 UTC
    -
    - -

    -And sometimes a local time can be ambiguous, mapping to more than one UTC time: -

    - -
    -try
    -{
    -    auto distant_past = locate_zone("America/New_York")->to_sys(day_point(sep/30/1945) + 2h - 1ns);
    -    cout << distant_past << " UTC\n";
    -}
    -catch (const exception& e)
    -{
    -    cout << e.what() << '\n';
    -}
    -
    - -
    -1945-09-30 01:59:59.999999999 is ambiguous.  It could be
    -1945-09-30 01:59:59.999999999 EPT == 1945-09-30 05:59:59.999999999 UTC or
    -1945-09-30 01:59:59.999999999 EST == 1945-09-30 06:59:59.999999999 UTC
    -
    - -
    - -

    -If you would rather not deal with these rare exceptions, you can choose ahead of time -to select the earliest time or latest time when a local time falls into a gap: -

    - -
    -auto z = locate_zone("America/New_York");
    -auto distant_past = z->to_sys(day_point(sep/30/1945) + 2h - 1ns, choose::earliest);
    -cout << distant_past << " UTC\n";
    -distant_past =      z->to_sys(day_point(sep/30/1945) + 2h - 1ns, choose::latest);
    -cout << distant_past << " UTC\n";
    -
    - -

    -Which outputs: -

    - -
    -1945-09-30 05:59:59.999999999 UTC
    -1945-09-30 06:59:59.999999999 UTC
    -
    - -

    -When using this form of to_sys and the local time is non-existent, both -choices will map to the single UTC time on either side of the gap: -

    - -
    -auto z = locate_zone("America/New_York");
    -auto distant_past = z->to_sys(day_point(feb/9/1942) + 3h - 1ms, choose::earliest);
    -cout << distant_past << " UTC\n";
    -distant_past =      z->to_sys(day_point(feb/9/1942) + 3h - 1ms, choose::latest);
    -cout << distant_past << " UTC\n";
    -
    - -

    -Which outputs: -

    - -
    -1942-02-09 07:00:00.000 UTC
    -1942-02-09 07:00:00.000 UTC
    -
    - -
    - -

    -So far I've shown how given a Zone and a system_clock::time_point -of arbitrary precision, you can use to_local to map UTC to local time, and -to_sys to map local time to UTC, with your choice of either detecting any -errors, or choosing how to resolve errors. But what if that is not enough? You may be -thinking: Do I have to call these mapping functions every second? How often does the -offset change? -

    - -

    -This library offers a partial solution to this dilemma. If the location you are concerned -about doesn't change, and if the database isn't reloaded, then get_info can -tell you how far into the past, and far into the future a given offset and abbreviation -are guaranteed to stay valid: -

    - -
    -template <class Rep, class Period>
    -sys_info
    -get_info(std::chrono::time_point<std::chrono::system_clock,
    -                                 std::chrono::duration<Rep, Period>> tp,
    -         tz timezone) const;
    -
    - -

    -Input a time_point tp, and indicate whether tp represents a UTC -time_point (tz::utc) or a local time_point -(tz::local), and a struct sys_info for that time_point -is returned: -

    - -
    -auto sys_info = locate_zone("America/New_York")->get_info(system_clock::now(), tz::utc);
    -
    - -

    -Upon return sys_info will contain the following information: -

    - -
      -
    • -sys_info.offset has type std::chrono::seconds and indicates the -current offset from UTC. A positive offset indicates that local time is ahead of UTC and -a negative offset indicates that local time is behind UTC. -

    • - -
    • -sys_info.abbrev has type std::string and indicates the -current abbreviation for the local time zone. -

    • - -
    • -sys_info.begin has type second_point and indicates the first -instant guaranteed to have this same offset and abbrev. The -time_point sys_info.begin is implicitly in the UTC time zone. Note that it is -possible that the instant prior to begin may or may not have a -different offset or abbrev. -

    • - -
    • -sys_info.end has type second_point and indicates the last instant -before which it is guaranteed to have this same offset and -abbrev. The time_point sys_info.end is implicitly in the UTC time -zone. The offset and abbrev associated with -sys_info.end and beyond may or may not be different. -

    • - -
    • -sys_info.save has type std::chrono::minutes and indicates the -amount of time that daylight savings time has moved the current offset. This can be -used to detect whether or not daylight savings is in effect (no if the value is 0min). -Note that save is already incorporated into the value of offset, -so you don't have to look at this field to get the current offset. This field exists -just in the spirit of more information is better. -

    • -
    - -

    -The sys_info also has a streaming operator which is mainly useful for debugging -purposes. Here is sample code and output: -

    - -
    -cout << current_zone()->get_info(system_clock::now(), tz::utc);
    -
    -2015-03-08 07:00:00
    -2015-11-01 06:00:00
    --04:00:00
    -01:00
    -EDT
    -
    - -

    -This is considered to be a low-level function, and as such there is no error detection -if you input a local time that either does not exist, or is ambiguous. Enough information -is returned for you to compute those conditions. Indeed, this is exactly how error -detection is computed in to_sys: by calling get_info and -analyzing how the input time relates to begin and end. -

    - -

    -Additionally the Zone is equality and less-than comparable (using the -name()). And you can stream the Zone out to a stream, though -the output may not be crystal clear. The streaming output is mainly used as an aid in -debugging this library, not your code. -

    - -

    Flight Example

    - -

    -There's nothing like a real-world example to help demonstrate things. Imagine a -plane flying from New York, New York, USA to Tehran, Iran. To make it more realistic, -lets say this flight occurred before the hostage crisis, right at the end of 1978. Flight -time for a non-stop one way trip is 14 hours and 44 minutes. -

    - -

    -Given that the departure is one minute past noon on Dec. 30, 1978, local time, what is -the local arrival time? +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.

    @@ -1108,75 +564,62 @@ the local arrival time?
     int
     main()
     {
    -    using namespace std::chrono;
    +    using namespace std::chrono_literals;
         using namespace date;
    -    auto nyc_tz = locate_zone("America/New_York");
    -    auto teh_tz = locate_zone("Asia/Tehran");
    -    auto nyc_departure_sys = nyc_tz->to_sys(day_point(dec/30/1978) + 12h + 1min);
    -    auto nyc_departure = nyc_tz->to_local(nyc_departure_sys);
    +
    +    auto departure = make_zoned("America/New_York", local_days{dec/30/1978} + 12h + 1min);
         auto flight_length = 14h + 44min;
    -    auto teh_arrival_sys = nyc_departure_sys + flight_length;
    -    auto teh_arrival = teh_tz->to_local(teh_arrival_sys);
    -    std::cout << "departure NYC time:  " << nyc_departure.first << ' '
    -                                         << nyc_departure.second << '\n';
    -    std::cout << "flight time is " << make_time(flight_length) << '\n';
    -    std::cout << "arrival Tehran time: " << teh_arrival.first << ' '
    -                                         << teh_arrival.second << '\n';
    +    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';
     }
     
    -

    -There are several points to be made about the above code: -

    - - -

    The output of the above program is:

     departure NYC time:  1978-12-30 12:01:00 EST
    -flight time is 14:44
    +flight time is       14:44
     arrival Tehran time: 1978-12-31 11:45:00 IRST
     

    -And this program is exactly correct. But what happens with the same flight on -the following day? +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 nyc_departure_sys = nyc_tz->to_sys(day_point(dec/31/1978) + 12h + 1min);
    +auto departure = make_zoned("America/New_York", local_days{dec/31/1978} + 12h + 1min);
    +
    -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 +

    +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
     

    @@ -1184,121 +627,8 @@ 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. See Flight Example with leap seconds. -

    - -

    utc_clock

    - -

    -One of the first questions everyone asks when a new date-time library comes out is: -

    - -

    -Does it handle leap seconds? -

    - -

    -The answer here is yes, this library can handle leap seconds. But be careful what you ask -for. Correctly handling leap seconds is error prone. Therefore this library handles leap -seconds in a completely different type-safe way, which can't be accidentally mixed with -everything else presented so far. The motivation for this separation is born from several -issues: -

    - - - -

    -utc_clock is a std::chrono-conforming clock with the same -duration as your system_clock, and a now() -function that returns the actual number of physical seconds since 1970-01-01 00:00:00 UTC -(counting leap seconds): -

    - -
    -class 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
    -        std::chrono::time_point<utc_clock,
    -            typename std::common_type<Duration, std::chrono::seconds>::type>
    -        sys_to_utc(std::chrono::time_point<std::chrono::system_clock, Duration> t);
    -
    -    template <class Duration>
    -        static
    -        std::chrono::time_point<std::chrono::system_clock,
    -            typename std::common_type<Duration, std::chrono::seconds>::type>
    -        utc_to_sys(std::chrono::time_point<utc_clock, Duration> t);
    -};
    -
    - -

    -Additionally utc_clock has static member functions for converting between -utc_clock-based time_points to and from -system_clock-based time_points of any precision. But it is -important to remember that utc_clock isn't connected to a super accurate -atomic clock. All it does is look its time_point up in the database to -see how many leap seconds have passed since 1972, and adds or subtracts that number of -seconds to do the conversion. The utc_clock::now() function simply calls -system_clock::now() and adds the current total of leaps seconds (currently -26) to the result. This is useful behavior but it is important to understand that -utc_clock is not a highly accurate scientific instrument. It is precisely -as accurate as your existing std::chrono::system_clock. -

    - -

    Flight Example with leap seconds

    - -

    -In the preceding section a flight from New York City to Tehran was offered, demonstrating -how local political changes in the rules governing UTC offsets can affect time -computations. As it turns out, while that flight departing on dec/31/1978 -was in the air, we also underwent a leap second addition. How does that impact the -computation, and how can this library be used to account for that (should it actually be -important)? +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:

    @@ -1308,409 +638,114 @@ important)?
     int
     main()
     {
    -    using namespace std::chrono;
    +    using namespace std::chrono_literals;
         using namespace date;
    -    auto nyc_tz = locate_zone("America/New_York");
    -    auto teh_tz = locate_zone("Asia/Tehran");
    -    auto nyc_departure_sys = nyc_tz->to_sys(day_point(dec/31/1978) + 12h + 1min);
    -    auto nyc_departure = nyc_tz->to_local(nyc_departure_sys);
    -    auto nyc_departure_utc = utc_clock::sys_to_utc(nyc_departure_sys);
    -    auto flight_length = 14h + 44min;
    -    auto teh_arrival_utc = nyc_departure_utc + flight_length;
    -    auto teh_arrival_sys = utc_clock::utc_to_sys(teh_arrival_utc);
    -    auto teh_arrival = teh_tz->to_local(teh_arrival_sys);
    -    std::cout << "departure NYC time:  " << nyc_departure.first << ' '
    -                                         << nyc_departure.second << '\n';
    -    std::cout << "flight time is " << make_time(flight_length) << '\n';
    -    std::cout << "arrival Tehran time: " << teh_arrival.first << ' '
    -                                         << teh_arrival.second << '\n';
    -}
     
    +    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
    +flight time is       14:44
    +arrival Tehran time: 1979-01-01 11:14:59 IRST
     

    -As can be seen, we now report an arrival time 1s before the arrival time we computed -without taking leap seconds into account. The key to working with leap seconds is to make -sure that all your time arithmetic takes place using utc_clock-based -time_points, instead of system_clock-based -time_points. Just convert to system_clock when you're ready to -break the date and time up into field-based structures, or are ready to further convert it -into a local time_point. In this example, the only time arithmetic is: +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:

    -auto teh_arrival_utc = nyc_departure_utc + flight_length;
    +Sun Sep 16 01:03:52 -0500 1973
     

    -The reset of the code is simply about converting from local, to system_clock -to utc_clock and back. +Into strings that look like this:

    -

    -Digression: Doing computations with leap seconds is cool. But perhaps the true -power of this library is revealed in the ease with which I created this example. I sat -back and said to myself: -

    -

    -I want to find a time and location where a timezone offset changed within 12 hours -of a leap second insertion. And then build my flight time example around that event. -

    -

    -Subsequently I wrote the following code to search the entire planet, and the last 45 -years, to find these rare chronological events: -

    -const auto& db = get_tzdb();
    -for (auto const& leap : db.leaps)
    -{
    -    for (auto const& zone : db.zones)
    -    {
    -        auto info = zone.get_info(leap.date(), tz::utc);
    -        if (leap.date() - info.begin <= 12h)
    -        {
    -            auto prev = zone.get_info(info.begin - 1s, tz::utc);
    -            if (prev.offset != info.offset)
    -                std::cout << zone.name() << "  " << info.begin << " : "
    -                          <<  leap << ' '
    -                          << make_time(info.offset-prev.offset) << '\n';
    -        }
    -        if (info.end - leap.date() <= 12h)
    -        {
    -            auto next = zone.get_info(info.end, tz::utc);
    -            if (next.offset != info.offset)
    -                std::cout << zone.name() << " " << info.end <<  " : "
    -                          <<  leap << ' '
    -                          << make_time(next.offset - info.offset) << '\n';
    -        }
    -    }
    -}
    -
    -

    -The flight time example wasn't really about Iran, the US, and politics after all. It was -about finding this needle in a haystack of time and space, which turned out to be -relatively easy and incredibly efficient. -

    -

    -You too can analyze the IANA Time Zone -Database in creative and interesting ways no one else has thought of. There is a lot -of history here. -

    - -

    Formatting

    - -

    -All of the types in this library, as well as in -date.h are streamable when you need quick and -simple output. However in addition to this simplistic streaming there is more -sophisticated formatting built on top of the C++11 time_put<char> -facet. time_put<char> itself is built on C's strftime -function. But time_put<char> is sensitive to C++ locales. -

    - -

    -The basic way to use formatting is to call the format function like this: -

    - -
    -cout << format("%A %F %T", floor<seconds>(system_clock::now())) << '\n';
    -
    - -

    -Which just output for me: -

    - -
    -Sunday 2016-04-03 22:02:19
    -
    - -

    -Note the cast to seconds precision in the call. This is how you control -the precision of the seconds output (if any). The modifiers %S -and %T will output seconds to whatever the precision is of the -time_point. For example: -

    - -
    -cout << format("%A %F %T", floor<milliseconds>(system_clock::now())) << '\n';
    -
    - -

    -would instead output: -

    - -
    -Sunday 2016-04-03 22:02:19.656
    -
    - -

    -Note that there is an implicit time zone being used here: UTC. The %z and -%Z modifiers can be used to show this: -

    - -
    -cout << format("%A %F %T %z %Z", floor<milliseconds>(system_clock::now())) << '\n';
    -
    - -

    -would instead output: -

    - -
    -Sunday 2016-04-03 22:02:19.656 +0000 UTC
    -
    - -

    -A Zone can also be passed in and then the %z and -%Z modifiers will reflect that passed-in zone. It is important to -remember however that format never shifts the -time_point for you. Instead you pass in a Zone that you know -to be associated with your time_point. For example: -

    - -
    -auto zone = locate_zone("Europe/Berlin");
    -auto local = zone->to_local(floor<milliseconds>(system_clock::now())).first;
    -cout << format("%A %F %T %z %Z", local, zone) << '\n';
    -
    - -
    -Monday 2016-04-04 00:02:19.656 +0200 CEST
    -
    - -

    -The only thing format ever does with a Zone is extract -the offset and/or the abbreviation for use with the %z and %Z -modifiers. -

    - -

    -You can also pass in a locale to format: -

    - -
    -cout << format(locale("de_DE"), "%A %F %T %z %Z", local, zone) << '\n';
    -
    - -
    -Montag 2016-04-04 00:02:19,656 +0200 CEST
    -
    - -

    -The set of named locales that your OS supports is defined by your OS, not this library. -

    - -

    -Instead of a time_point you can also pass in anything that is implicitly -convertible to day_point: -

    - -
    -cout << format(locale("de_DE"), "%A %B %e, %Y", 2016_y/jul/mon[1]) << '\n';
    -
    - -
    -Montag Juli  4, 2016
    -
    - -

    -In summary, use format by passing in a format string and a -time_point, or something implicitly convertible to a day_point. -You can optionally pass in a locale as the first parameter, and a -Zone as the last parameter. format will never alter the value -of your time_point. The precision of the time_point controls -the precision of seconds with the %S and %T modifiers. If you -pass in a Zone, this will only impact the output of %z and -%Z (which default to +0000 and UTC respectively). -The output of format is a std::string. -

    - -

    Parsing

    - -

    -Since all parts of all date-types in this library can be constructed with integral types, -you can parse any format you wish as integrals, and create dates from any format you -wish that way. -

    - -

    -However this section introduces a parse function which is built on top -of the C++11 time_get facet which can also be used: -

    - -
    -template <class Duration>
    -void
    -parse(std::istream& is, const std::string& format,
    -      std::chrono::time_point<std::chrono::system_clock, Duration>& tp);
    -
    - -

    -You can input any istream, and a format string much like that used for -format and strftime, and a time_point of any -precision, and this function will attempt to extract the time_point from -the istream by using the format string. If not successful, -the time_point will not be altered. -

    - -

    -Example use: -

    - -
    -istringstream is("Montag 2016-04-04 00:02:19,656 +0200");
    -is.imbue(locale("de_DE"));
    -system_clock::time_point tp;
    -parse(is, "%A %F %T %z", tp);
    -cout << tp << '\n';
    -
    - -

    -Which outputs: -

    - -
    -2016-04-03 22:02:19.656000
    -
    - -

    -Note that the locale associated with the istream is respected. -If the format string contains a %z which matches the input stream, this is -used to convert the value to UTC. If there is no %z, then no conversion -happens (you can assume whatever timezone you want). Note that fractional seconds are -accepted as long as one uses %T or %S, and the precision of the -time_point is fine enough to accept fractional seconds. -

    - -

    -%Z is not accepted as the mapping from a timezone abbreviation to UTC is -in general, ambiguous. If you have a %Z in the format string, this will -result in is.fail() returning true after the call to -parse. -

    - -

    -However, if you absolutely must parse a timestamp with a timezone abbreviation in it, -an extra parse overload is provided: -

    - -
    -template <class Duration>
    -void
    -parse(std::istream& is, const std::string& format,
    -      std::chrono::time_point<std::chrono::system_clock, Duration>& tp,
    -      std::string& abbrev);
    -
    - -

    -Now if %Z matches a word in is and if the rest of -is correctly parses according to format, then -abbrev will be assigned the word which matched %Z. This -will not have any impact on the value of tp (no timezone offset -applied). However perhaps there is enough a-priori knowledge in your application to -make use of the value of abbrev to correctly interpret the meaning of -the timestamp and the resulting value of tp. -

    - -

    -As an example of how this option can be both useful and dangerous, consider -an example where we need to parse the timestamp "Thu Apr 07 11:45:28 AEST 2016", and -we want to discover what the corresponding time is in UTC, and what timezone this -timestamp represents. -

    - -

    -The following program parses this, and then searches -the entire timezone database looking for timezones which have "AEST" as an abbreviation -at a local time of Apr 07 11:45:28 2016. The program finds the first one, notes its UTC -offset, and then searches for more. If it finds more, and the UTC offset is the same, -it simply outputs the name of each additional timezone found. If the additional timezones -have a different UTC offset, that is noted too by outputting the UTC timestamp associated -with the additional timezone. -

    - -
    -#include "tz.h"
    -#include <string>
    -#include <iostream>
    -#include <sstream>
    -#include <cassert>
    -
    -int
    -main()
    +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;
    -    auto& db = get_tzdb();
    -    std::istringstream in("Thu Apr 07 11:45:28 AEST 2016");
    -    time_point<system_clock, seconds> tp_local;
    -    std::string abbrev;
    -    parse(in, "%a %b %d %T %Z %Y", tp_local, abbrev);
    -    assert(!in.fail());
    -    auto i = std::find_if(db.zones.begin(), db.zones.end(),
    -                          [&tp_local, &abbrev](auto const& z)
    -                          {
    -                              return z.get_info(tp_local, tz::local).abbrev == abbrev;
    -                          });
    -    if (i != db.zones.end())
    -    {
    -        auto tp_utc = i->to_sys(tp_local);
    -        std::cout << tp_utc << " UTC " << i->name() << '\n';
    -        for (++i; i != db.zones.end(); ++i)
    -        {
    -            if (i->get_info(tp_local, tz::local).abbrev != abbrev)
    -                continue;
    -            auto tp = i->to_sys(tp_local);
    -            if (tp != tp_utc)
    -                std::cout << tp << " UTC ";
    -            std::cout << i->name() << '\n';
    -        }
    -    }
    +    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);
     }
     

    -This program outputs: -

    - -
    -2016-04-07 01:45:28 UTC Australia/Brisbane
    -Australia/Currie
    -Australia/Hobart
    -Australia/Lindeman
    -Australia/Melbourne
    -Australia/Sydney
    -
    - -

    -This indicates that "Thu Apr 07 11:45:28 AEST 2016" unambiguously refers to -2016-04-07 01:45:28 UTC (a UTC offset of +1000). However which IANA timezone is referred -to is ambiguous. This means that past or future timepoints using any of these timezones -may or may not have the same UTC offsets (or abbreviations) among this set of timezones. +Let's walk through this:

    -And this is a good case. Consider just altering the abbreviation in the above example -from AEST to BST. Now the output is: -

    - -
    -2016-04-07 10:45:28 UTC Europe/London
    -2016-04-07 00:45:28 UTC Pacific/Bougainville
    -
    - -

    -Meaning: Not only do we not know what timezone this refers to, it could mean one of -two different UTC timepoints! +First, date::parse works with istreams so you can parse from +files, from strings, or anything else that is an istream.

    -So in summary, it is dangerous to parse timezone abbreviations. You should avoid it if at -all possible. However, if you are forced to, this library has the power to find out every -thing that is knowable about that timestamp. +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.

    Reference