#ifndef TZ_H #define TZ_H // The MIT License (MIT) // // Copyright (c) 2015, 2016 Howard Hinnant // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // // Our apologies. When the previous paragraph was written, lowercase had not yet // been invented (that woud involve another several millennia of evolution). // We did not mean to shout. // Get more recent database at http://www.iana.org/time-zones // The notion of "current timezone" is something the operating system is expected to "just // know". How it knows this is system specific. It's often a value set by the user at OS // intallation time and recorded by the OS somewhere. On Linux and Mac systems the current // timezone name is obtained by looking at the name or contents of a particular file on // disk. On Windows the current timzeone name comes from the registry. In either method, // there is no guarantee that the "native" current timezone name obtained will match any // of the "Standard" names in this library's "database". On Linux, the names usually do // seem to match so mapping functions to map from native to "Standard" are typically not // required. On Windows, the names are never "Standard" so mapping is always required. // Technically any OS may use the mapping process but currently only Windows does use it. #ifdef _WIN32 # ifndef TIMEZONE_MAPPING # define TIMEZONE_MAPPING 1 # endif #else # ifdef TIMEZONE_MAPPING # error "Timezone mapping is not required or not implemented for this platform." # endif #endif #ifndef LAZY_INIT # define LAZY_INIT 1 #endif #ifndef HAS_REMOTE_API # ifdef _WIN32 # define HAS_REMOTE_API 0 # else # define HAS_REMOTE_API 1 # endif #endif #ifndef AUTO_DOWNLOAD # define AUTO_DOWNLOAD HAS_REMOTE_API #endif static_assert(HAS_REMOTE_API == 0 ? AUTO_DOWNLOAD == 0 : true, "AUTO_DOWNLOAD can not be turned on without HAS_REMOTE_API"); #ifndef USE_SHELL_API # define USE_SHELL_API 0 #endif #include "date.h" #if defined(_MSC_VER) && (_MSC_VER < 1900) #include "tz_private.h" #endif #include #include #include #include #include #if LAZY_INIT # include # include #endif #include #include #include #include #include #include #include namespace date { enum class choose {earliest, latest}; namespace detail { struct undocumented; } class nonexistent_local_time : public std::runtime_error { public: template nonexistent_local_time(local_time tp, local_seconds first, const std::string& first_abbrev, local_seconds last, const std::string& last_abbrev, sys_seconds time_sys); private: template static std::string make_msg(local_time tp, local_seconds first, const std::string& first_abbrev, local_seconds last, const std::string& last_abbrev, sys_seconds time_sys); }; template inline nonexistent_local_time::nonexistent_local_time(local_time tp, local_seconds first, const std::string& first_abbrev, local_seconds last, const std::string& last_abbrev, sys_seconds time_sys) : std::runtime_error(make_msg(tp, first, first_abbrev, last, last_abbrev, time_sys)) {} template std::string nonexistent_local_time::make_msg(local_time tp, local_seconds first, const std::string& first_abbrev, local_seconds last, const std::string& last_abbrev, sys_seconds time_sys) { using namespace date; std::ostringstream os; os << tp << " is in a gap between\n" << first << ' ' << first_abbrev << " and\n" << last << ' ' << last_abbrev << " which are both equivalent to\n" << time_sys << " UTC"; return os.str(); } class ambiguous_local_time : public std::runtime_error { public: template ambiguous_local_time(local_time tp, std::chrono::seconds first_offset, const std::string& first_abbrev, std::chrono::seconds second_offset, const std::string& second_abbrev); private: template static std::string make_msg(local_time tp, std::chrono::seconds first_offset, const std::string& first_abbrev, std::chrono::seconds second_offset, const std::string& second_abbrev); }; template inline ambiguous_local_time::ambiguous_local_time( local_time tp, std::chrono::seconds first_offset, const std::string& first_abbrev, std::chrono::seconds second_offset, const std::string& second_abbrev) : std::runtime_error(make_msg(tp, first_offset, first_abbrev, second_offset, second_abbrev)) {} template std::string ambiguous_local_time::make_msg(local_time tp, std::chrono::seconds first_offset, const std::string& first_abbrev, std::chrono::seconds second_offset, const std::string& second_abbrev) { using namespace date; std::ostringstream os; os << tp << " is ambiguous. It could be\n" << tp << ' ' << first_abbrev << " == " << tp - first_offset << " UTC or\n" << tp << ' ' << second_abbrev << " == " << tp - second_offset << " UTC"; return os.str(); } namespace detail { class Rule; } struct sys_info { sys_seconds begin; sys_seconds end; std::chrono::seconds offset; std::chrono::minutes save; std::string abbrev; }; std::ostream& operator<<(std::ostream& os, const sys_info& r); struct local_info { enum {unique, nonexistent, ambiguous} result; sys_info first; sys_info second; }; std::ostream& operator<<(std::ostream& os, const local_info& r); class time_zone; template class zoned_time { const time_zone* zone_; sys_time tp_; public: zoned_time(sys_time st); explicit zoned_time(const time_zone* z); explicit zoned_time(const std::string& name); template , sys_time>::value >::type> zoned_time(const zoned_time& zt) NOEXCEPT; zoned_time(const time_zone* z, local_time tp); zoned_time(const std::string& name, local_time tp); zoned_time(const time_zone* z, local_time tp, choose c); zoned_time(const std::string& name, local_time tp, choose c); zoned_time(const time_zone* z, const zoned_time& zt); zoned_time(const std::string& name, const zoned_time& zt); zoned_time(const time_zone* z, const zoned_time& zt, choose); zoned_time(const std::string& name, const zoned_time& zt, choose); zoned_time(const time_zone* z, const sys_time& st); zoned_time(const std::string& name, const sys_time& st); zoned_time& operator=(sys_time st); zoned_time& operator=(local_time ut); operator sys_time() const; explicit operator local_time() const; const time_zone* get_time_zone() const; local_time get_local_time() const; sys_time get_sys_time() const; sys_info get_info() const; template friend bool operator==(const zoned_time& x, const zoned_time& y); template friend std::basic_ostream& operator<<(std::basic_ostream& os, const zoned_time& t); private: template friend class zoned_time; static_assert(std::is_convertible::value, "zoned_time must have a precision of seconds or finer"); }; using zoned_seconds = zoned_time; template inline bool operator==(const zoned_time& x, const zoned_time& y) { return x.zone_ == y.zone_ && x.tp_ == y.tp_; } template inline bool operator!=(const zoned_time& x, const zoned_time& y) { return !(x == y); } #if !defined(_MSC_VER) || (_MSC_VER >= 1900) namespace detail { struct zonelet; } #endif class time_zone { private: std::string name_; std::vector zonelets_; #if LAZY_INIT std::unique_ptr adjusted_; #endif public: #if !defined(_MSC_VER) || (_MSC_VER >= 1900) time_zone(time_zone&&) = default; time_zone& operator=(time_zone&&) = default; #else // defined(_MSC_VER) && (_MSC_VER < 1900) time_zone(time_zone&& src); time_zone& operator=(time_zone&& src); #endif // defined(_MSC_VER) && (_MSC_VER < 1900) explicit time_zone(const std::string& s, detail::undocumented); const std::string& name() const; template sys_info get_info(sys_time st) const; template local_info get_info(local_time tp) const; template sys_time::type> to_sys(local_time tp) const; template sys_time::type> to_sys(local_time tp, choose z) const; template local_time::type> to_local(sys_time tp) const; friend bool operator==(const time_zone& x, const time_zone& y); friend bool operator< (const time_zone& x, const time_zone& y); friend std::ostream& operator<<(std::ostream& os, const time_zone& z); void add(const std::string& s); void adjust_infos(const std::vector& rules); private: sys_info get_info_impl(sys_seconds tp) const; local_info get_info_impl(local_seconds tp) const; sys_info get_info_impl(sys_seconds tp, int timezone) const; void parse_info(std::istream& in); template sys_time::type> to_sys_impl(local_time tp, choose z, std::false_type) const; template sys_time::type> to_sys_impl(local_time tp, choose, std::true_type) const; }; #if defined(_MSC_VER) && (_MSC_VER < 1900) inline time_zone::time_zone(time_zone&& src) : name_(std::move(src.name_)) , zonelets_(std::move(src.zonelets_)) #if LAZY_INIT , adjusted_(std::move(src.adjusted_)) #endif {} inline time_zone& time_zone::operator=(time_zone&& src) { name_ = std::move(src.name_); zonelets_ = std::move(src.zonelets_); #if LAZY_INIT adjusted_ = std::move(src.adjusted_); #endif return *this; } #endif // defined(_MSC_VER) && (_MSC_VER < 1900) inline const std::string& time_zone::name() const { return name_; } template inline sys_info time_zone::get_info(sys_time st) const { using namespace std::chrono; return get_info_impl(floor(st)); } template inline local_info time_zone::get_info(local_time tp) const { using namespace std::chrono; return get_info_impl(floor(tp)); } template inline sys_time::type> time_zone::to_sys(local_time tp) const { return to_sys_impl(tp, choose{}, std::true_type{}); } template inline sys_time::type> time_zone::to_sys(local_time tp, choose z) const { return to_sys_impl(tp, z, std::false_type{}); } template inline local_time::type> time_zone::to_local(sys_time tp) const { using LT = local_time::type>; auto i = get_info(tp); return LT{(tp + i.offset).time_since_epoch()}; } inline bool operator==(const time_zone& x, const time_zone& y) {return x.name_ == y.name_;} inline bool operator< (const time_zone& x, const time_zone& y) {return x.name_ < y.name_;} inline bool operator!=(const time_zone& x, const time_zone& y) {return !(x == y);} inline bool operator> (const time_zone& x, const time_zone& y) {return y < x;} inline bool operator<=(const time_zone& x, const time_zone& y) {return !(y < x);} inline bool operator>=(const time_zone& x, const time_zone& y) {return !(x < y);} template sys_time::type> time_zone::to_sys_impl(local_time tp, choose z, std::false_type) const { using namespace date; using namespace std::chrono; auto i = get_info(tp); if (i.result == local_info::nonexistent) { return i.first.end; } else if (i.result == local_info::ambiguous) { if (z == choose::latest) return sys_time{tp.time_since_epoch()} - i.second.offset; } return sys_time{tp.time_since_epoch()} - i.first.offset; } template sys_time::type> time_zone::to_sys_impl(local_time tp, choose, std::true_type) const { using namespace date; using namespace std::chrono; auto i = get_info(tp); if (i.result == local_info::nonexistent) { auto prev_end = local_seconds{i.first.end.time_since_epoch()} + i.first.offset; auto next_begin = local_seconds{i.second.begin.time_since_epoch()} + i.second.offset; throw nonexistent_local_time(tp, prev_end, i.first.abbrev, next_begin, i.second.abbrev, i.first.end); } else if (i.result == local_info::ambiguous) { throw ambiguous_local_time(tp, i.first.offset, i.first.abbrev, i.second.offset, i.second.abbrev); } return sys_time{tp.time_since_epoch()} - i.first.offset; } class link { private: std::string name_; std::string target_; public: explicit link(const std::string& s); const std::string& name() const {return name_;} const std::string& target() const {return target_;} friend bool operator==(const link& x, const link& y) {return x.name_ == y.name_;} friend bool operator< (const link& x, const link& y) {return x.name_ < y.name_;} friend std::ostream& operator<<(std::ostream& os, const link& x); }; inline bool operator!=(const link& x, const link& y) {return !(x == y);} inline bool operator> (const link& x, const link& y) {return y < x;} inline bool operator<=(const link& x, const link& y) {return !(y < x);} inline bool operator>=(const link& x, const link& y) {return !(x < y);} class leap { private: sys_seconds date_; public: explicit leap(const std::string& s, detail::undocumented); sys_seconds date() const {return date_;} friend bool operator==(const leap& x, const leap& y) {return x.date_ == y.date_;} friend bool operator< (const leap& x, const leap& y) {return x.date_ < y.date_;} template friend bool operator==(const leap& x, const sys_time& y) { return x.date_ == y; } template friend bool operator< (const leap& x, const sys_time& y) { return x.date_ < y; } template friend bool operator< (const sys_time& x, const leap& y) { return x < y.date_; } friend std::ostream& operator<<(std::ostream& os, const leap& x); }; inline bool operator!=(const leap& x, const leap& y) {return !(x == y);} inline bool operator> (const leap& x, const leap& y) {return y < x;} inline bool operator<=(const leap& x, const leap& y) {return !(y < x);} inline bool operator>=(const leap& x, const leap& y) {return !(x < y);} template inline bool operator==(const sys_time& x, const leap& y) { return y == x; } template inline bool operator!=(const leap& x, const sys_time& y) { return !(x == y); } template inline bool operator!=(const sys_time& x, const leap& y) { return !(x == y); } template inline bool operator> (const leap& x, const sys_time& y) { return y < x; } template inline bool operator> (const sys_time& x, const leap& y) { return y < x; } template inline bool operator<=(const leap& x, const sys_time& y) { return !(y < x); } template inline bool operator<=(const sys_time& x, const leap& y) { return !(y < x); } template inline bool operator>=(const leap& x, const sys_time& y) { return !(x < y); } template inline bool operator>=(const sys_time& x, const leap& y) { return !(x < y); } #ifdef TIMEZONE_MAPPING namespace detail { // The time zone mapping is modelled after this data file: // http://unicode.org/repos/cldr/trunk/common/supplemental/windowsZones.xml // and the field names match the element names from the mapZone element // of windowsZones.xml. // The website displays this file here: // http://www.unicode.org/cldr/charts/latest/supplemental/zone_tzid.html // The html view is sorted before being displayed but is otherwise the same // There is a mapping between the os centric view (in this case windows) // the html displays uses and the generic view the xml file. // That mapping is this: // display column "windows" -> xml field "other". // display column "region" -> xml field "territory". // display column "tzid" -> xml field "type". // This structure uses the generic terminology because it could be // used to to support other os/native name conversions, not just windows, // and using the same generic names helps retain the connection to the // origin of the data that we are using. struct timezone_mapping { timezone_mapping(const char* other, const char* territory, const char* type) : other(other), territory(territory), type(type) { } timezone_mapping() = default; std::string other; std::string territory; std::string type; }; struct timezone_info { timezone_info() = default; std::string timezone_id; std::string standard_name; }; } // detail #endif // TIMEZONE_MAPPING struct TZ_DB { std::string version; std::vector zones; std::vector links; std::vector leaps; std::vector rules; #ifdef TIMEZONE_MAPPING // TODO! These need some protection. std::vector mappings; std::vector native_zones; #endif TZ_DB() = default; #if !defined(_MSC_VER) || (_MSC_VER >= 1900) TZ_DB(TZ_DB&&) = default; TZ_DB& operator=(TZ_DB&&) = default; #else // defined(_MSC_VER) || (_MSC_VER >= 1900) TZ_DB(TZ_DB&& src) : version(std::move(src.version)), zones(std::move(src.zones)), links(std::move(src.links)), leaps(std::move(src.leaps)), rules(std::move(src.rules)) #ifdef TIMEZONE_MAPPING , mappings(std::move(src.mappings)), native_zones(std::move(src.native_zones)) #endif {} TZ_DB& operator=(TZ_DB&& src) { version = std::move(src.version); zones = std::move(src.zones); links = std::move(src.links); leaps = std::move(src.leaps); rules = std::move(src.rules); #ifdef TIMEZONE_MAPPING mappings = std::move(src.mappings); native_zones = std::move(src.native_zones); #endif return *this; } #endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) }; std::ostream& operator<<(std::ostream& os, const TZ_DB& db); const TZ_DB& get_tzdb(); const TZ_DB& reload_tzdb(); #if HAS_REMOTE_API std::string remote_version(); bool remote_download(const std::string& version); bool remote_install(const std::string& version); #endif const time_zone* locate_zone(const std::string& tz_name); #ifdef TZ_TEST # if _WIN32 const time_zone* locate_native_zone(const std::string& native_tz_name); # endif // _WIN32 #endif // TZ_TEST const time_zone* current_zone(); // zoned_time template inline zoned_time::zoned_time(sys_time st) : zone_(locate_zone("UTC")) , tp_(st) {} template inline zoned_time::zoned_time(const time_zone* z) : zone_(z) {assert(zone_ != nullptr);} template inline zoned_time::zoned_time(const std::string& name) : zoned_time(locate_zone(name)) {} template inline zoned_time::zoned_time(const time_zone* z, local_time t) : zone_(z) , tp_(z->to_sys(t)) {} template inline zoned_time::zoned_time(const std::string& name, local_time t) : zoned_time(locate_zone(name), t) {} template inline zoned_time::zoned_time(const time_zone* z, local_time t, choose c) : zone_(z) , tp_(z->to_sys(t, c)) {} template inline zoned_time::zoned_time(const std::string& name, local_time t, choose c) : zoned_time(locate_zone(name), t, c) {} template template inline zoned_time::zoned_time(const zoned_time& zt) NOEXCEPT : zone_(zt.zone_) , tp_(zt.tp_) {} template inline zoned_time::zoned_time(const time_zone* z, const zoned_time& zt) : zone_(z) , tp_(zt.tp_) {} template inline zoned_time::zoned_time(const std::string& name, const zoned_time& zt) : zoned_time(locate_zone(name), zt) {} template inline zoned_time::zoned_time(const time_zone* z, const zoned_time& zt, choose) : zoned_time(z, zt) {} template inline zoned_time::zoned_time(const std::string& name, const zoned_time& zt, choose c) : zoned_time(locate_zone(name), zt, c) {} template inline zoned_time::zoned_time(const time_zone* z, const sys_time& st) : zone_(z) , tp_(st) {} template inline zoned_time::zoned_time(const std::string& name, const sys_time& st) : zoned_time(locate_zone(name), st) {} template inline zoned_time& zoned_time::operator=(sys_time st) { tp_ = st; return *this; } template inline zoned_time& zoned_time::operator=(local_time ut) { tp_ = zone_->to_sys(ut); return *this; } template inline zoned_time::operator local_time() const { return get_local_time(); } template inline zoned_time::operator sys_time() const { return get_sys_time(); } template inline const time_zone* zoned_time::get_time_zone() const { return zone_; } template inline local_time zoned_time::get_local_time() const { return zone_->to_local(tp_); } template inline sys_time zoned_time::get_sys_time() const { return tp_; } template inline sys_info zoned_time::get_info() const { return zone_->get_info(tp_); } // make_zoned_time template inline zoned_time::type> make_zoned(sys_time tp) { return {tp}; } template inline zoned_time::type> make_zoned(const time_zone* zone, local_time tp) { return {zone, tp}; } template inline zoned_time::type> make_zoned(const std::string& name, local_time tp) { return {name, tp}; } template inline zoned_time::type> make_zoned(const time_zone* zone, local_time tp, choose c) { return {zone, tp, c}; } template inline zoned_time::type> make_zoned(const std::string& name, local_time tp, choose c) { return {name, tp, c}; } template inline zoned_time::type> make_zoned(const time_zone* zone, const zoned_time& zt) { return {zone, zt}; } template inline zoned_time::type> make_zoned(const std::string& name, const zoned_time& zt) { return {name, zt}; } template inline zoned_time::type> make_zoned(const time_zone* zone, const zoned_time& zt, choose c) { return {zone, zt, c}; } template inline zoned_time::type> make_zoned(const std::string& name, const zoned_time& zt, choose c) { return {name, zt, c}; } template inline zoned_time::type> make_zoned(const time_zone* zone, const sys_time& st) { return {zone, st}; } template inline zoned_time::type> make_zoned(const std::string& name, const sys_time& st) { return {name, st}; } template inline std::basic_ostream& operator<<(std::basic_ostream& os, const zoned_time& t) { auto i = t.zone_->get_info(t.tp_); auto lt = t.tp_ + i.offset; return os << lt << ' ' << i.abbrev; } 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; static CONSTDATA bool is_steady = true; static time_point now() NOEXCEPT; template static std::chrono::time_point::type> sys_to_utc(sys_time t); template static sys_time::type> utc_to_sys(std::chrono::time_point t); }; template using utc_time = std::chrono::time_point; using utc_seconds = utc_time; inline utc_clock::time_point utc_clock::now() NOEXCEPT { using namespace std::chrono; return sys_to_utc(system_clock::now()); } template utc_time::type> utc_clock::sys_to_utc(sys_time t) { using namespace std::chrono; using duration = typename std::common_type::type; auto const& leaps = get_tzdb().leaps; auto const lt = std::upper_bound(leaps.begin(), leaps.end(), t); return utc_time{t.time_since_epoch() + seconds{lt-leaps.begin()}}; } template sys_time::type> utc_clock::utc_to_sys(utc_time t) { using namespace std::chrono; using duration = typename std::common_type::type; auto const& leaps = get_tzdb().leaps; auto tp = sys_time{t.time_since_epoch()}; if (tp >= leaps.front()) { auto const lt = std::upper_bound(leaps.begin(), leaps.end(), tp); tp -= seconds{lt-leaps.begin()}; if (tp < lt[-1]) { if (tp >= lt[-1].date() - seconds{1}) tp = lt[-1].date() - duration{1}; else tp += seconds{1}; } } return tp; } template inline sys_time::type> to_sys_time(utc_time ut) { return utc_clock::utc_to_sys(ut); } template inline utc_time::type> to_utc_time(sys_time st) { return utc_clock::sys_to_utc(st); } template std::basic_ostream& operator<<(std::basic_ostream& os, const utc_time& t) { using namespace std::chrono; using duration = typename std::common_type::type; auto const& leaps = get_tzdb().leaps; auto tp = sys_time{t.time_since_epoch()}; if (tp >= leaps.front()) { auto const lt = std::upper_bound(leaps.begin(), leaps.end(), tp); tp -= seconds{lt-leaps.begin()}; if (tp < lt[-1]) { if (tp >= lt[-1].date() - seconds{1}) { auto const dp = floor(tp); auto time = make_time(tp-dp); time.seconds() += seconds{1}; return os << year_month_day(dp) << ' ' << time; } else tp += seconds{1}; } } return os << tp; } // tai_clock class tai_clock { public: using duration = std::chrono::system_clock::duration; using rep = duration::rep; using period = duration::period; using time_point = std::chrono::time_point; static const bool is_steady = true; static time_point now() NOEXCEPT; template static std::chrono::time_point::type> utc_to_tai(utc_time t); template static utc_time::type> tai_to_utc(std::chrono::time_point t); }; template using tai_time = std::chrono::time_point; using tai_seconds = tai_time; template tai_time::type> tai_clock::utc_to_tai(utc_time t) { using namespace std::chrono; using duration = typename std::common_type::type; return tai_time{t.time_since_epoch()} + (sys_days{year{1970}/jan/1} - sys_days{year{1958}/jan/1} + seconds{10}); } template utc_time::type> tai_clock::tai_to_utc(tai_time t) { using namespace std::chrono; using duration = typename std::common_type::type; return utc_time{t.time_since_epoch()} - (sys_days{year{1970}/jan/1} - sys_days{year{1958}/jan/1} + seconds{10}); } template inline utc_time::type> to_utc_time(tai_time t) { return tai_clock::tai_to_utc(t); } template inline tai_time::type> to_tai_time(utc_time t) { return tai_clock::utc_to_tai(t); } inline tai_clock::time_point tai_clock::now() NOEXCEPT { using namespace std::chrono; return to_tai_time(utc_clock::now()); } template std::basic_ostream& operator<<(std::basic_ostream& os, const tai_time& t) { using namespace std::chrono; using duration = typename std::common_type::type; auto tp = sys_time{t.time_since_epoch()} - (sys_days{year{1970}/jan/1} - sys_days{year{1958}/jan/1}); return os << tp; } // gps_clock class gps_clock { public: using duration = std::chrono::system_clock::duration; using rep = duration::rep; using period = duration::period; using time_point = std::chrono::time_point; static const bool is_steady = true; static time_point now() NOEXCEPT; template static std::chrono::time_point::type> utc_to_gps(utc_time t); template static utc_time::type> gps_to_utc(std::chrono::time_point t); }; template using gps_time = std::chrono::time_point; using gps_seconds = gps_time; template gps_time::type> gps_clock::utc_to_gps(utc_time t) { using namespace std::chrono; using duration = typename std::common_type::type; return gps_time{t.time_since_epoch()} - (sys_days{year{1980}/jan/sun[1]} - sys_days{year{1970}/jan/1} + seconds{9}); } template utc_time::type> gps_clock::gps_to_utc(gps_time t) { using namespace std::chrono; using duration = typename std::common_type::type; return utc_time{t.time_since_epoch()} + (sys_days{year{1980}/jan/sun[1]} - sys_days{year{1970}/jan/1} + seconds{9}); } template inline utc_time::type> to_utc_time(gps_time t) { return gps_clock::gps_to_utc(t); } template inline gps_time::type> to_gps_time(utc_time t) { return gps_clock::utc_to_gps(t); } inline gps_clock::time_point gps_clock::now() NOEXCEPT { using namespace std::chrono; return to_gps_time(utc_clock::now()); } template std::basic_ostream& operator<<(std::basic_ostream& os, const gps_time& t) { using namespace std::chrono; using duration = typename std::common_type::type; auto tp = sys_time{t.time_since_epoch()} + (sys_days{year{1980}/jan/sun[1]} - sys_days{year{1970}/jan/1}); return os << tp; } template inline sys_time::type> to_sys_time(tai_time t) { return to_sys_time(to_utc_time(t)); } template inline sys_time::type> to_sys_time(gps_time t) { return to_sys_time(to_utc_time(t)); } template inline tai_time::type> to_tai_time(sys_time t) { return to_tai_time(to_utc_time(t)); } template inline tai_time::type> to_tai_time(gps_time t) { return to_tai_time(to_utc_time(t)); } template inline gps_time::type> to_gps_time(sys_time t) { return to_gps_time(to_utc_time(t)); } template inline gps_time::type> to_gps_time(tai_time t) { return to_gps_time(to_utc_time(t)); } // format template inline std::basic_string format(const std::locale& loc, std::basic_string fmt, const zoned_time& tp) { auto const info = tp.get_info(); return detail::format(loc, std::move(fmt), tp.get_local_time(), &info.abbrev, &info.offset); } template inline std::basic_string format(std::basic_string fmt, const zoned_time& tp) { auto const info = tp.get_info(); return detail::format(std::locale{}, std::move(fmt), tp.get_local_time(), &info.abbrev, &info.offset); } // const CharT* formats template inline std::basic_string format(const std::locale& loc, const CharT* fmt, const zoned_time& tp) { auto const info = tp.get_info(); return detail::format(loc, std::basic_string(fmt), tp.get_local_time(), &info.abbrev, &info.offset); } template inline std::basic_string format(const CharT* fmt, const zoned_time& tp) { auto const info = tp.get_info(); return detail::format(std::locale{}, std::basic_string(fmt), tp.get_local_time(), &info.abbrev, &info.offset); } } // namespace date #endif // TZ_H