#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 // Questions: // 1. Reload database. // 4. Is the utc to sys renaming complete? Was it done correctly? /* 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 #endif #ifndef LAZY_INIT # define LAZY_INIT 1 #endif #ifndef HAS_REMOTE_API # ifndef _MSC_VER # define HAS_REMOTE_API 1 # else # define HAS_REMOTE_API 0 # 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"); #include "date.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if LAZY_INIT # include # include #endif namespace date { enum class choose {earliest, latest}; namespace detail { class 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(); } 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: 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); } class time_zone { private: struct zonelet; 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::earliest) 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); } #if 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; #if 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)) #if 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); #if 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 #ifdef _WIN32 const time_zone* locate_native_zone(const std::string& native_tz_name); #endif #endif 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()}; auto const lt = std::upper_bound(leaps.begin(), leaps.end(), tp); tp -= seconds{lt-leaps.begin()}; if (lt != leaps.begin() && tp + seconds{1} < lt[-1]) 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); } // format namespace detail { template std::basic_string format(const std::locale& loc, std::basic_string format, local_time tp, const time_zone* zone = nullptr) { // Handle these specially // %S append fractional seconds if tp has precision finer than seconds // %T append fractional seconds if tp has precision finer than seconds // %z replace with offset from zone // %Z replace with abbreviation from zone using namespace std; using namespace std::chrono; for (std::size_t i = 0; i < format.size(); ++i) { if (format[i] == '%' && i < format.size()-1) { switch (format[i+1]) { case 'S': case 'T': if (ratio_less>::value) { basic_ostringstream os; os.imbue(loc); os << make_time(tp - floor(tp)); auto s = os.str(); s.erase(0, 8); format.insert(i+2, s); i += 2 + s.size() - 1; } break; case 'z': if (zone == nullptr) throw std::runtime_error("Can not format local_time with %z"); else { auto info = zone->get_info(tp).first; auto offset = duration_cast(info.offset); basic_ostringstream os; if (offset >= minutes{0}) os << '+'; os << make_time(offset); auto s = os.str(); s.erase(s.find(':'), 1); format.replace(i, 2, s); i += s.size() - 1; } break; case 'Z': if (zone == nullptr) throw std::runtime_error("Can not format local_time with %z"); else { auto info = zone->get_info(tp).first; format.replace(i, 2, std::basic_string (info.abbrev.begin(), info.abbrev.end())); i += info.abbrev.size() - 1; } break; } } } auto& f = use_facet>(loc); basic_ostringstream os; auto tt = system_clock::to_time_t(sys_time{tp.time_since_epoch()}); std::tm tm{}; #ifndef _MSC_VER gmtime_r(&tt, &tm); #else gmtime_s(&tm, &tt); #endif f.put(os, os, os.fill(), &tm, format.data(), format.data() + format.size()); return os.str(); } } // namespace detail template inline std::basic_string format(const std::locale& loc, std::basic_string format, local_time tp) { return detail::format(loc, std::move(format), tp); } template inline std::basic_string format(std::basic_string format, local_time tp) { return detail::format(std::locale{}, std::move(format), tp); } template inline std::basic_string format(const std::locale& loc, std::basic_string format, const zoned_time& tp) { return detail::format(loc, std::move(format), tp.get_local_time(), tp.get_time_zone()); } template inline std::basic_string format(std::basic_string format, const zoned_time& tp) { return detail::format(std::locale{}, std::move(format), tp.get_local_time(), tp.get_time_zone()); } template inline std::basic_string format(const std::locale& loc, std::basic_string format, sys_time tp) { return detail::format(loc, std::move(format), local_time{tp.time_since_epoch()}, locate_zone("UTC")); } template inline std::basic_string format(std::basic_string format, sys_time tp) { return detail::format(std::locale{}, std::move(format), local_time{tp.time_since_epoch()}, locate_zone("UTC")); } // const CharT* formats template inline std::basic_string format(const std::locale& loc, const CharT* format, local_time tp) { return detail::format(loc, std::basic_string(format), tp); } template inline std::basic_string format(const CharT* format, local_time tp) { return detail::format(std::locale{}, std::basic_string(format), tp); } template inline std::basic_string format(const std::locale& loc, const CharT* format, const zoned_time& tp) { return detail::format(loc, std::basic_string(format), tp.get_local_time(), tp.get_time_zone()); } template inline std::basic_string format(const CharT* format, const zoned_time& tp) { return detail::format(std::locale{}, std::basic_string(format), tp.get_local_time(), tp.get_time_zone()); } template inline std::basic_string format(const std::locale& loc, const CharT* format, sys_time tp) { return detail::format(loc, std::basic_string(format), local_time{tp.time_since_epoch()}, locate_zone("UTC")); } template inline std::basic_string format(const CharT* format, sys_time tp) { return detail::format(std::locale{}, std::basic_string(format), local_time{tp.time_since_epoch()}, locate_zone("UTC")); } // parse namespace detail { template void parse(std::basic_istream& is, const std::basic_string& format, sys_time& tp, std::basic_string& abbrev, std::chrono::minutes& offset) { using namespace std; using namespace std::chrono; typename basic_istream::sentry ok{is}; if (ok) { auto& f = use_facet>(is.getloc()); ios_base::iostate err = ios_base::goodbit; std::tm tm{}; Duration subseconds{}; std::basic_string temp_abbrev; minutes temp_offset{}; auto b = format.data(); auto i = b; auto e = b + format.size(); for (; i < e; ++i) { if (*i == '%' && i < e-1) { switch (i[1]) { case 'T': case 'S': f.get(is, 0, is, err, &tm, b, i); ++i; b = i+1; if (*i == 'T') { const CharT hm[] = {'%', 'H', ':', '%', 'M', ':', 0}; f.get(is, 0, is, err, &tm, hm, hm); } if (ratio_less>::value) { double s; is >> s; if (!is.fail()) subseconds = round(duration{s}); else err |= ios_base::failbit; } else { const CharT hm[] = {'%', 'S'}; f.get(is, 0, is, err, &tm, hm, hm+2); } break; case 'z': f.get(is, 0, is, err, &tm, b, i); ++i; b = i+1; if ((err & ios_base::failbit) == 0) { CharT sign{}; is >> sign; if (!is.fail() && (sign == '+' || sign == '-')) { char h1, h0, m1, m0; h1 = static_cast(is.get()); h0 = static_cast(is.get()); m1 = static_cast(is.get()); m0 = static_cast(is.get()); if (!is.fail() && std::isdigit(h1) && std::isdigit(h0) && std::isdigit(m1) && std::isdigit(m0)) { temp_offset = 10*hours{h1 - '0'} + hours{h0 - '0'} + 10*minutes{m1 - '0'} + minutes{m0 - '0'}; if (sign == '-') temp_offset = -temp_offset; } else err |= ios_base::failbit; } else err |= ios_base::failbit; } break; case 'Z': f.get(is, 0, is, err, &tm, b, i); ++i; b = i+1; if ((err & ios_base::failbit) == 0) { is >> temp_abbrev; if (is.fail()) err |= ios_base::failbit; } break; } } } if ((err & ios_base::failbit) == 0) { if (b < e) f.get(is, 0, is, err, &tm, b, e); if ((err & ios_base::failbit) == 0) { #ifndef _MSC_VER auto tt = timegm(&tm); #else auto tt = _mkgmtime(&tm); #endif tp = floor(system_clock::from_time_t(tt) + subseconds); abbrev = std::move(temp_abbrev); offset = temp_offset; } } is.setstate(err); } } } // namespace detail template inline void parse(std::basic_istream& is, const std::basic_string& format, sys_time& tp) { std::basic_string abbrev; std::chrono::minutes offset{}; detail::parse(is, format, tp, abbrev, offset); if (!is.fail()) tp = floor(tp - offset); } template inline void parse(std::basic_istream& is, const std::basic_string& format, sys_time& tp, std::basic_string& abbrev) { std::chrono::minutes offset{}; detail::parse(is, format, tp, abbrev, offset); if (!is.fail()) tp = floor(tp - offset); } template inline void parse(std::basic_istream& is, const std::basic_string& format, sys_time& tp, std::chrono::minutes& offset) { std::basic_string abbrev; detail::parse(is, format, tp, abbrev, offset); if (!is.fail()) tp = floor(tp - offset); } template inline void parse(std::basic_istream& is, const std::basic_string& format, sys_time& tp, std::basic_string& abbrev, std::chrono::minutes& offset) { detail::parse(is, format, tp, abbrev, offset); if (!is.fail()) tp = floor(tp - offset); } template inline void parse(std::basic_istream& is, const std::basic_string& format, sys_time& tp, std::chrono::minutes& offset, std::basic_string& abbrev) { detail::parse(is, format, tp, abbrev, offset); if (!is.fail()) tp = floor(tp - offset); } template inline void parse(std::basic_istream& is, const std::basic_string& format, local_time& tp) { sys_time st; std::basic_string abbrev; std::chrono::minutes offset{}; detail::parse(is, format, st, abbrev, offset); if (!is.fail()) tp = local_time{st.time_since_epoch()}; } template inline void parse(std::basic_istream& is, const std::basic_string& format, local_time& tp, std::basic_string& abbrev) { sys_time st; std::chrono::minutes offset{}; detail::parse(is, format, st, abbrev, offset); if (!is.fail()) tp = local_time{st.time_since_epoch()}; } template inline void parse(std::basic_istream& is, const std::basic_string& format, local_time& tp, std::chrono::minutes& offset) { sys_time st; std::basic_string abbrev; detail::parse(is, format, st, abbrev, offset); if (!is.fail()) tp = local_time{st.time_since_epoch()}; } template inline void parse(std::basic_istream& is, const std::basic_string& format, local_time& tp, std::basic_string& abbrev, std::chrono::minutes& offset) { sys_time st; detail::parse(is, format, st, abbrev, offset); if (!is.fail()) tp = local_time{st.time_since_epoch()}; } template inline void parse(std::basic_istream& is, const std::basic_string& format, local_time& tp, std::chrono::minutes& offset, std::basic_string& abbrev) { sys_time st; detail::parse(is, format, st, abbrev, offset); if (!is.fail()) tp = local_time{st.time_since_epoch()}; } // const CharT* formats template inline void parse(std::basic_istream& is, const CharT* format, sys_time& tp) { parse(is, std::basic_string(format), tp); } template inline void parse(std::basic_istream& is, const CharT* format, sys_time& tp, std::basic_string& abbrev) { parse(is, std::basic_string(format), tp, abbrev); } template inline void parse(std::basic_istream& is, const CharT* format, sys_time& tp, std::chrono::minutes& offset) { parse(is, std::basic_string(format), tp, offset); } template inline void parse(std::basic_istream& is, const CharT* format, sys_time& tp, std::basic_string& abbrev, std::chrono::minutes& offset) { parse(is, std::basic_string(format), tp, abbrev, offset); } template inline void parse(std::basic_istream& is, const CharT* format, sys_time& tp, std::chrono::minutes& offset, std::basic_string& abbrev) { parse(is, std::basic_string(format), tp, abbrev, offset); } template inline void parse(std::basic_istream& is, const CharT* format, local_time& tp) { parse(is, std::basic_string(format), tp); } template inline void parse(std::basic_istream& is, const CharT* format, local_time& tp, std::basic_string& abbrev) { parse(is, std::basic_string(format), tp, abbrev); } template inline void parse(std::basic_istream& is, const CharT* format, local_time& tp, std::chrono::minutes& offset) { parse(is, std::basic_string(format), tp, offset); } template inline void parse(std::basic_istream& is, const CharT* format, local_time& tp, std::basic_string& abbrev, std::chrono::minutes& offset) { parse(is, std::basic_string(format), tp, abbrev, offset); } template inline void parse(std::basic_istream& is, const CharT* format, local_time& tp, std::chrono::minutes& offset, std::basic_string& abbrev) { parse(is, std::basic_string(format), tp, abbrev, offset); } } // namespace date #endif // TZ_H