#ifndef TZ_H #define TZ_H // The MIT License (MIT) // // Copyright (c) 2015, 2016, 2017 Howard Hinnant // Copyright (c) 2017 Jiangang Zhuang // Copyright (c) 2017 Aaron Bishop // // 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 would 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 // installation 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 timezone 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. #ifndef USE_OS_TZDB # define USE_OS_TZDB 0 #endif #ifndef HAS_REMOTE_API # if USE_OS_TZDB == 0 # ifdef _WIN32 # define HAS_REMOTE_API 0 # else # define HAS_REMOTE_API 1 # endif # else // HAS_REMOTE_API makes no since when using the OS timezone database # define HAS_REMOTE_API 0 # endif #endif static_assert(!(USE_OS_TZDB && HAS_REMOTE_API), "USE_OS_TZDB and HAS_REMOTE_API can not be used together"); #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 1 #endif #if USE_OS_TZDB # ifdef _WIN32 # error "USE_OS_TZDB can not be used on Windows" # endif # ifndef MISSING_LEAP_SECONDS # ifdef __APPLE__ # define MISSING_LEAP_SECONDS 1 # else # define MISSING_LEAP_SECONDS 0 # endif # endif #else # define MISSING_LEAP_SECONDS 0 #endif #include "date.h" #if defined(_MSC_VER) && (_MSC_VER < 1900) #include "tz_private.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef _WIN32 # ifdef DATE_BUILD_DLL # define DATE_API __declspec(dllexport) # elif defined(DATE_BUILD_LIB) # define DATE_API # else # define DATE_API __declspec(dllimport) # endif #else # define DATE_API #endif 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 begin, const std::string& first_abbrev, local_seconds end, const std::string& last_abbrev, sys_seconds time_sys) : std::runtime_error(make_msg(tp, begin, first_abbrev, end, last_abbrev, time_sys)) {} template std::string nonexistent_local_time::make_msg(local_time tp, local_seconds begin, const std::string& first_abbrev, local_seconds end, const std::string& last_abbrev, sys_seconds time_sys) { using namespace date; std::ostringstream os; os << tp << " is in a gap between\n" << begin << ' ' << first_abbrev << " and\n" << end << ' ' << 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(); } struct sys_info { sys_seconds begin; sys_seconds end; std::chrono::seconds offset; std::chrono::minutes save; std::string abbrev; }; template std::basic_ostream& operator<<(std::basic_ostream& os, const sys_info& r) { os << r.begin << '\n'; os << r.end << '\n'; os << make_time(r.offset) << "\n"; os << make_time(r.save) << "\n"; os << r.abbrev << '\n'; return os; } struct local_info { enum {unique, nonexistent, ambiguous} result; sys_info first; sys_info second; }; template std::basic_ostream& operator<<(std::basic_ostream& os, const local_info& r) { if (r.result == local_info::nonexistent) os << "nonexistent between\n"; else if (r.result == local_info::ambiguous) os << "ambiguous between\n"; os << r.first; if (r.result != local_info::unique) { os << "and\n"; os << r.second; } return os; } class time_zone; DATE_API const time_zone* locate_zone(const std::string& tz_name); DATE_API const time_zone* current_zone(); template class zoned_time { public: using duration = typename std::common_type::type; private: const time_zone* zone_; sys_time tp_; public: zoned_time(); zoned_time(const 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, const local_time& tp); zoned_time(const std::string& name, const local_time& tp); zoned_time(const char* name, const local_time& tp); zoned_time(const time_zone* z, const local_time& tp, choose c); zoned_time(const std::string& name, const local_time& tp, choose c); zoned_time(const char* name, const 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 char* 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 char* 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(const char* name, const sys_time& st); zoned_time& operator=(const sys_time& st); zoned_time& operator=(const 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; }; 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 { # if USE_OS_TZDB struct transition; struct expanded_ttinfo; # else // !USE_OS_TZDB struct zonelet; class Rule; # endif // !USE_OS_TZDB } #endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) class time_zone { private: std::string name_; #if USE_OS_TZDB std::vector transitions_; std::vector ttinfos_; #else // !USE_OS_TZDB std::vector zonelets_; #endif // !USE_OS_TZDB std::unique_ptr adjusted_; 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) DATE_API explicit time_zone(const std::string& s, detail::undocumented); const std::string& name() const NOEXCEPT; 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) NOEXCEPT; friend bool operator< (const time_zone& x, const time_zone& y) NOEXCEPT; friend DATE_API std::ostream& operator<<(std::ostream& os, const time_zone& z); #if !USE_OS_TZDB DATE_API void add(const std::string& s); #endif // !USE_OS_TZDB private: DATE_API sys_info get_info_impl(sys_seconds tp) const; DATE_API local_info get_info_impl(local_seconds tp) const; 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 USE_OS_TZDB DATE_API void init() const; DATE_API void init_impl(); DATE_API sys_info load_sys_info(std::vector::const_iterator i) const; template DATE_API void load_data(std::istream& inf, std::int32_t tzh_leapcnt, std::int32_t tzh_timecnt, std::int32_t tzh_typecnt, std::int32_t tzh_charcnt); #else // !USE_OS_TZDB DATE_API sys_info get_info_impl(sys_seconds tp, int timezone) const; DATE_API void adjust_infos(const std::vector& rules); DATE_API void parse_info(std::istream& in); #endif // !USE_OS_TZDB }; #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_)) , adjusted_(std::move(src.adjusted_)) {} inline time_zone& time_zone::operator=(time_zone&& src) { name_ = std::move(src.name_); zonelets_ = std::move(src.zonelets_); adjusted_ = std::move(src.adjusted_); return *this; } #endif // defined(_MSC_VER) && (_MSC_VER < 1900) inline const std::string& time_zone::name() const NOEXCEPT { return name_; } template inline sys_info time_zone::get_info(sys_time st) const { using namespace std::chrono; return get_info_impl(date::floor(st)); } template inline local_info time_zone::get_info(local_time tp) const { using namespace std::chrono; return get_info_impl(date::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) NOEXCEPT {return x.name_ == y.name_;} inline bool operator< (const time_zone& x, const time_zone& y) NOEXCEPT {return x.name_ < y.name_;} inline bool operator!=(const time_zone& x, const time_zone& y) NOEXCEPT {return !(x == y);} inline bool operator> (const time_zone& x, const time_zone& y) NOEXCEPT {return y < x;} inline bool operator<=(const time_zone& x, const time_zone& y) NOEXCEPT {return !(y < x);} inline bool operator>=(const time_zone& x, const time_zone& y) NOEXCEPT {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; } #if !USE_OS_TZDB class link { private: std::string name_; std::string target_; public: DATE_API 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 DATE_API 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);} #endif // !USE_OS_TZDB #if !MISSING_LEAP_SECONDS class leap { private: sys_seconds date_; public: #if USE_OS_TZDB DATE_API explicit leap(const sys_seconds& s, detail::undocumented); #else DATE_API explicit leap(const std::string& s, detail::undocumented); #endif 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 DATE_API 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); } #endif // !MISSING_LEAP_SECONDS #ifdef _WIN32 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; }; } // detail #endif // _WIN32 struct TZ_DB { std::string version = "unknown"; std::vector zones; #if !USE_OS_TZDB std::vector links; #endif #if !MISSING_LEAP_SECONDS std::vector leaps; #endif #if !USE_OS_TZDB std::vector rules; #endif #ifdef _WIN32 std::vector mappings; #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)) , mappings(std::move(src.mappings)) {} 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); mappings = std::move(src.mappings); return *this; } #endif // defined(_MSC_VER) && (_MSC_VER < 1900) }; DATE_API std::ostream& operator<<(std::ostream& os, const TZ_DB& db); DATE_API const TZ_DB& get_tzdb(); #if !USE_OS_TZDB DATE_API const TZ_DB& reload_tzdb(); DATE_API void set_install(const std::string& install); #endif // !USE_OS_TZDB #if HAS_REMOTE_API DATE_API std::string remote_version(); DATE_API bool remote_download(const std::string& version); DATE_API bool remote_install(const std::string& version); #endif DATE_API const time_zone* locate_zone(const std::string& tz_name); DATE_API const time_zone* current_zone(); // zoned_time template inline zoned_time::zoned_time() : zone_(locate_zone("UTC")) {} template inline zoned_time::zoned_time(const 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 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 local_time& t) : zone_(z) , tp_(z->to_sys(t)) {} template inline zoned_time::zoned_time(const std::string& name, const local_time& t) : zoned_time(locate_zone(name), t) {} template inline zoned_time::zoned_time(const char* name, const local_time& t) : zoned_time(locate_zone(name), t) {} template inline zoned_time::zoned_time(const time_zone* z, const local_time& t, choose c) : zone_(z) , tp_(z->to_sys(t, c)) {} template inline zoned_time::zoned_time(const std::string& name, const local_time& t, choose c) : zoned_time(locate_zone(name), t, c) {} template inline zoned_time::zoned_time(const char* name, const local_time& t, choose c) : zoned_time(locate_zone(name), t, c) {} 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 char* 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 char* 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(const char* name, const sys_time& st) : zoned_time(locate_zone(name), st) {} template inline zoned_time& zoned_time::operator=(const sys_time& st) { tp_ = st; return *this; } template inline zoned_time& zoned_time::operator=(const 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::duration> zoned_time::get_local_time() const { return zone_->to_local(tp_); } template inline sys_time::duration> 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(const sys_time& tp) { return {tp}; } template inline zoned_time::type> make_zoned(const time_zone* zone, const local_time& tp) { return {zone, tp}; } template inline zoned_time::type> make_zoned(const std::string& name, const local_time& tp) { return {name, tp}; } template inline zoned_time::type> make_zoned(const time_zone* zone, const local_time& tp, choose c) { return {zone, tp, c}; } template inline zoned_time::type> make_zoned(const std::string& name, const 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 std::basic_ostream& to_stream(std::basic_ostream& os, const CharT* fmt, const zoned_time& tp) { auto const info = tp.get_info(); return to_stream(os, fmt, tp.get_local_time(), &info.abbrev, &info.offset); } template inline std::basic_ostream& operator<<(std::basic_ostream& os, const zoned_time& t) { const CharT fmt[] = {'%', 'F', ' ', '%', 'T', CharT{}}; return to_stream(os, fmt, t); } #if !MISSING_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; static CONSTDATA bool is_steady = false; static time_point now(); }; template using utc_time = std::chrono::time_point; using utc_seconds = utc_time; template inline utc_time::type> to_utc_time(const sys_time& st) { 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(), st); return utc_time{st.time_since_epoch() + seconds{lt-leaps.begin()}}; } // Return pair // first is true if ut is during a leap second insertion, otherwise false. // If ut is during a leap second insertion, that leap second is included in the count template std::pair is_leap_second(date::utc_time const& ut) { using namespace date; using namespace std::chrono; using duration = typename std::common_type::type; auto const& leaps = get_tzdb().leaps; auto tp = sys_time{ut.time_since_epoch()}; auto const lt = std::upper_bound(leaps.begin(), leaps.end(), tp); auto ds = seconds{lt-leaps.begin()}; tp -= ds; auto ls = false; if (lt > leaps.begin()) { if (tp < lt[-1]) { if (tp >= lt[-1].date() - seconds{1}) ls = true; else --ds; } } return {ls, ds}; } template inline sys_time::type> to_sys_time(const utc_time& ut) { using namespace std::chrono; using duration = typename std::common_type::type; auto ls = is_leap_second(ut); auto tp = sys_time{ut.time_since_epoch() - ls.second}; if (ls.first) tp = floor(tp) + seconds{1} - duration{1}; return tp; } inline utc_clock::time_point utc_clock::now() { using namespace std::chrono; return to_utc_time(system_clock::now()); } template std::basic_ostream& to_stream(std::basic_ostream& os, const CharT* fmt, const utc_time& t) { using namespace std; using namespace std::chrono; using CT = typename common_type::type; const string abbrev("UTC"); CONSTDATA seconds offset{0}; auto ls = is_leap_second(t); auto tp = sys_time{t.time_since_epoch() - ls.second}; auto const sd = floor(tp); year_month_day ymd = sd; auto time = make_time(tp - sd); time.seconds() += seconds{ls.first}; fields fds{ymd, time}; return to_stream(os, fmt, fds, &abbrev, &offset); } template std::basic_ostream& operator<<(std::basic_ostream& os, const utc_time& t) { const CharT fmt[] = {'%', 'F', ' ', '%', 'T', CharT{}}; return to_stream(os, fmt, t); } template > std::basic_istream& from_stream(std::basic_istream& is, const CharT* fmt, utc_time& tp, std::basic_string* abbrev = nullptr, std::chrono::minutes* offset = nullptr) { using namespace std; using namespace std::chrono; using CT = typename common_type::type; minutes offset_local{}; auto offptr = offset ? offset : &offset_local; fields fds{}; from_stream(is, fmt, fds, abbrev, offptr); if (!fds.ymd.ok()) is.setstate(ios::failbit); if (!is.fail()) { bool is_60_sec = fds.tod.seconds() == seconds{60}; if (is_60_sec) fds.tod.seconds() -= seconds{1}; auto tmp = to_utc_time(sys_days(fds.ymd) + (fds.tod.to_duration() - *offptr)); if (is_60_sec) tmp += seconds{1}; if (is_60_sec != is_leap_second(tmp).first || !fds.tod.in_conventional_range()) { is.setstate(ios::failbit); return is; } tp = time_point_cast(tmp); } return is; } // 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 = false; static time_point now() NOEXCEPT; }; template using tai_time = std::chrono::time_point; using tai_seconds = tai_time; template inline utc_time::type> to_utc_time(const tai_time& t) NOEXCEPT { 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 tai_time::type> to_tai_time(const utc_time& t) NOEXCEPT { 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 inline tai_time::type> to_tai_time(const sys_time& t) { return to_tai_time(to_utc_time(t)); } inline tai_clock::time_point tai_clock::now() NOEXCEPT { using namespace std::chrono; return to_tai_time(system_clock::now()); } template std::basic_ostream& to_stream(std::basic_ostream& os, const CharT* fmt, const tai_time& t) { using namespace std; using namespace std::chrono; using CT = typename common_type::type; const string abbrev("TAI"); CONSTDATA seconds offset{0}; auto tp = sys_time{t.time_since_epoch()} - (sys_days(year{1970}/jan/1) - sys_days(year{1958}/jan/1)); auto const sd = floor(tp); year_month_day ymd = sd; auto time = make_time(tp - sd); fields fds{ymd, time}; return to_stream(os, fmt, fds, &abbrev, &offset); } template std::basic_ostream& operator<<(std::basic_ostream& os, const tai_time& t) { const CharT fmt[] = {'%', 'F', ' ', '%', 'T', CharT{}}; return to_stream(os, fmt, t); } template > std::basic_istream& from_stream(std::basic_istream& is, const CharT* fmt, tai_time& tp, std::basic_string* abbrev = nullptr, std::chrono::minutes* offset = nullptr) { using namespace std; using namespace std::chrono; using CT = typename common_type::type; minutes offset_local{}; auto offptr = offset ? offset : &offset_local; fields fds{}; from_stream(is, fmt, fds, abbrev, offptr); if (!fds.ymd.ok() || !fds.tod.in_conventional_range()) is.setstate(ios::failbit); if (!is.fail()) tp = tai_time{duration_cast( (sys_days(fds.ymd) + fds.tod.to_duration() + (sys_days(year{1970}/jan/1) - sys_days(year{1958}/jan/1)) - *offptr).time_since_epoch())}; return is; } // 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 = false; static time_point now() NOEXCEPT; }; template using gps_time = std::chrono::time_point; using gps_seconds = gps_time; template inline utc_time::type> to_utc_time(const gps_time& t) NOEXCEPT { 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 gps_time::type> to_gps_time(const 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 inline gps_time::type> to_gps_time(const sys_time& t) { return to_gps_time(to_utc_time(t)); } inline gps_clock::time_point gps_clock::now() NOEXCEPT { using namespace std::chrono; return to_gps_time(system_clock::now()); } template std::basic_ostream& to_stream(std::basic_ostream& os, const CharT* fmt, const gps_time& t) { using namespace std; using namespace std::chrono; using CT = typename common_type::type; const string abbrev("GPS"); CONSTDATA seconds offset{0}; auto tp = sys_time{t.time_since_epoch()} + (sys_days(year{1980}/jan/sun[1]) - sys_days(year{1970}/jan/1)); auto const sd = floor(tp); year_month_day ymd = sd; auto time = make_time(tp - sd); fields fds{ymd, time}; return to_stream(os, fmt, fds, &abbrev, &offset); } template std::basic_ostream& operator<<(std::basic_ostream& os, const gps_time& t) { const CharT fmt[] = {'%', 'F', ' ', '%', 'T', CharT{}}; return to_stream(os, fmt, t); } template > std::basic_istream& from_stream(std::basic_istream& is, const CharT* fmt, gps_time& tp, std::basic_string* abbrev = nullptr, std::chrono::minutes* offset = nullptr) { using namespace std; using namespace std::chrono; using CT = typename common_type::type; minutes offset_local{}; auto offptr = offset ? offset : &offset_local; fields fds{}; from_stream(is, fmt, fds, abbrev, offptr); if (!fds.ymd.ok() || !fds.tod.in_conventional_range()) is.setstate(ios::failbit); if (!is.fail()) tp = gps_time{duration_cast( (sys_days(fds.ymd) + fds.tod.to_duration() - (sys_days(year{1980}/jan/sun[1]) - sys_days(year{1970}/jan/1)) - *offptr).time_since_epoch())}; return is; } template inline sys_time::type> to_sys_time(const tai_time& t) { return to_sys_time(to_utc_time(t)); } template inline sys_time::type> to_sys_time(const gps_time& t) { return to_sys_time(to_utc_time(t)); } template inline tai_time::type> to_tai_time(const gps_time& t) NOEXCEPT { using namespace std::chrono; using duration = typename std::common_type::type; return tai_time{t.time_since_epoch()} + (sys_days(year{1980}/jan/sun[1]) - sys_days(year{1958}/jan/1) + seconds{19}); } template inline gps_time::type> to_gps_time(const tai_time& t) NOEXCEPT { 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{1958}/jan/1) + seconds{19}); } #endif // !MISSING_LEAP_SECONDS } // namespace date #endif // TZ_H