diff --git a/tz.cpp b/tz.cpp index 127d441..2562750 100644 --- a/tz.cpp +++ b/tz.cpp @@ -273,12 +273,14 @@ static void get_windows_timezone_info(std::vector& tz_list) // Open the parent time zone key that has the list of timezones in. reg_key zones_key; - static const wchar_t zones_key_name[] = { L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones" }; + static const wchar_t zones_key_name[] = + { L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones" }; result = zones_key.open(zones_key_name); // TODO! Review if this should happen here or be signalled later. // We don't want the process to fail on startup because of this. if (result != ERROR_SUCCESS) - throw std::runtime_error("Time Zone registry key could not be opened: " + get_win32_message(result)); + throw std::runtime_error("Time Zone registry key could not be opened: " + + get_win32_message(result)); DWORD size; wchar_t zone_key_name[256]; @@ -295,7 +297,8 @@ static void get_windows_timezone_info(std::vector& tz_list) auto status = RegEnumKeyExW(zones_key.handle(), zone_index, zone_key_name, &size, nullptr, nullptr, nullptr, nullptr); if (status != ERROR_SUCCESS && status != ERROR_NO_MORE_ITEMS) - throw std::runtime_error("Can't enumerate time zone registry key" + get_win32_message(status)); + throw std::runtime_error("Can't enumerate time zone registry key" + + get_win32_message(status)); if (status == ERROR_NO_MORE_ITEMS) break; zone_key_name[size] = L'\0'; @@ -389,25 +392,32 @@ load_timezone_mappings_from_csv_file(const std::string& input_path) std::string copyright; for (int i = 0; i < 4; ++i) getline(is, copyright); + for (;;) { timezone_mapping zm{}; - is >> std::quoted(zm.other); + char ch; + + is.read(&ch, 1); if (is.eof()) break; - + std::getline(is, zm.other, '\"'); read_field_delim(); - is >> std::quoted(zm.territory); - read_field_delim(); - is >> std::quoted(zm.type); - char record_delim; - is.read(&record_delim, 1); - if (is.gcount() != 1 || record_delim != '\n') + is.read(&ch, 1); + std::getline(is, zm.territory, '\"'); + read_field_delim(); + + is.read(&ch, 1); + std::getline(is, zm.type, '\"'); + + is.read(&ch, 1); + if (is.gcount() != 1 || ch != '\n') error("record delimiter LF expected"); if (is.fail() || is.eof()) error("unexpected end of file, file read error or formatting error."); + ++line; mappings.push_back(std::move(zm)); } @@ -416,7 +426,8 @@ load_timezone_mappings_from_csv_file(const std::string& input_path) } static bool -native_to_standard_timezone_name(const std::string& native_tz_name, std::string& standard_tz_name) +native_to_standard_timezone_name(const std::string& native_tz_name, + std::string& standard_tz_name) { // TOOD! Need be a case insensitive compare? if (native_tz_name == "UTC") @@ -757,7 +768,11 @@ operator>>(std::istream& is, MonthDayTime& x) throw std::runtime_error(std::string("bad operator: ") + c + c2 + std::to_string(d)); x.type_ = c == '<' ? MonthDayTime::lteq : MonthDayTime::gteq; - x.u = {date::month(m)/d, weekday(dow)}; +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) + x.u = {date::month(m)/d, date::weekday(dow)}; +#else + x.u = MonthDayTime::U(date::month(m)/d, date::weekday(dow)); +#endif } else throw std::runtime_error(std::string("bad operator: ") + c); @@ -1005,7 +1020,7 @@ operator<<(std::ostream& os, const Rule& r) { using namespace date; using namespace std::chrono; - save_stream _(os); + detail::save_stream _(os); os.fill(' '); os.flags(std::ios::dec | std::ios::left); os.width(15); @@ -1194,17 +1209,21 @@ Rule::split_overlaps(std::vector& rules) Zone::zonelet::~zonelet() { +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) using minutes = std::chrono::minutes; using string = std::string; if (tag_ == has_save) u.save_.~minutes(); else u.rule_.~string(); +#endif } Zone::zonelet::zonelet() { +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) ::new(&u.rule_) std::string(); +#endif } Zone::zonelet::zonelet(const zonelet& i) @@ -1221,10 +1240,17 @@ Zone::zonelet::zonelet(const zonelet& i) , first_rule_(i.first_rule_) , last_rule_(i.last_rule_) { +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) if (tag_ == has_save) ::new(&u.save_) std::chrono::minutes(i.u.save_); else ::new(&u.rule_) std::string(i.u.rule_); +#else + if (tag_ == has_save) + u.save_ = i.u.save_; + else + u.rule_ = i.u.rule_; +#endif } Zone::Zone(const std::string& s) @@ -1344,7 +1370,8 @@ find_previous_rule(const Rule* r, date::year y) // [first, last) all have the same name static std::pair -find_next_rule(const Rule* first_rule, const Rule* last_rule, const Rule* r, date::year y) +find_next_rule(const Rule* first_rule, const Rule* last_rule, const Rule* r, + date::year y) { using namespace date; if (y == r->ending_year()) @@ -1564,9 +1591,15 @@ Zone::adjust_infos(const std::vector& rules) std::istringstream in(z.u.rule_); in.exceptions(std::ios::failbit | std::ios::badbit); auto tmp = duration_cast(parse_signed_time(in)); +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) z.u.rule_.~string(); z.tag_ = zonelet::has_save; ::new(&z.u.save_) minutes(tmp); +#else + z.u.rule_.clear(); + z.tag_ = zonelet::has_save; + z.u.save_ = tmp; +#endif } catch (...) { @@ -1773,7 +1806,7 @@ operator<<(std::ostream& os, const Zone& z) { using namespace date; using namespace std::chrono; - save_stream _(os); + detail::save_stream _(os); os.fill(' '); os.flags(std::ios::dec | std::ios::left); os.width(35); @@ -1832,7 +1865,7 @@ std::ostream& operator<<(std::ostream& os, const Link& x) { using namespace date; - save_stream _(os); + detail::save_stream _(os); os.fill(' '); os.flags(std::ios::dec | std::ios::left); os.width(35); @@ -2099,7 +2132,8 @@ current_zone() if (tz_result == TIME_ZONE_ID_INVALID) { auto error_code = ::GetLastError(); // Store this quick before it gets overwritten. - throw std::runtime_error("GetTimeZoneInformation failed: " + get_win32_message(error_code)); + throw std::runtime_error("GetTimeZoneInformation failed: " + + get_win32_message(error_code)); } std::wstring_convert> converter; std::string standard_name(converter.to_bytes(tzi.StandardName)); diff --git a/tz.h b/tz.h index 162c8a5..1a073c0 100644 --- a/tz.h +++ b/tz.h @@ -201,8 +201,23 @@ private: std::vector zonelets_; public: +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) Zone(Zone&&) = default; Zone& operator=(Zone&&) = default; +#else + Zone(Zone&& src) + : + name_(std::move(src.name_)), + zonelets_(std::move(zonelets_)) + {} + + Zone& operator=(Zone&& src) + { + name_ = std::move(src.name_); + zonelets_ = std::move(src.zonelets_); + return *this; + } +#endif explicit Zone(const std::string& s); @@ -334,7 +349,8 @@ Zone::to_sys_impl(std::chrono::time_point(tp) >= i.begin + get_info(i.begin - seconds{1}, tz::utc).offset); + assert(floor(tp) >= + i.begin + get_info(i.begin - seconds{1}, tz::utc).offset); } if (i.end - floor(tp_sys) <= days{1}) { @@ -577,8 +593,36 @@ struct TZ_DB #endif TZ_DB() = default; +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) TZ_DB(TZ_DB&&) = default; TZ_DB& operator=(TZ_DB&&) = default; +#else + TZ_DB(TZ_DB&& src) + : + zones(std::move(src.zones)), + links(std::move(links)), + leaps(std::move(leaps)), + rules(std::move(rules)) +#if TIMEZONE_MAPPING + , + mappings(std::move(mappings)), + native_zones(std::move(native_zones)) +#endif + {} + + TZ_DB& operator=(TZ_DB&& src) + { + zones = std::move(src.zones); + links = std::move(links); + leaps = std::move(leaps); + rules = std::move(rules); +#if TIMEZONE_MAPPING + mappings = std::move(mappings); + native_zones = std::move(native_zones); +#endif + return *this; + } +#endif }; std::ostream& operator<<(std::ostream& os, const TZ_DB& db); @@ -604,7 +648,7 @@ public: using time_point = std::chrono::time_point; static CONSTDATA bool is_steady = true; - static time_point now() noexcept; + static time_point now() NOEXCEPT; template static @@ -621,7 +665,7 @@ public: inline utc_clock::time_point -utc_clock::now() noexcept +utc_clock::now() NOEXCEPT { using namespace std::chrono; return sys_to_utc(system_clock::now()); diff --git a/tz_private.h b/tz_private.h index f2954a7..c334892 100644 --- a/tz_private.h +++ b/tz_private.h @@ -33,6 +33,9 @@ class MonthDayTime private: struct pair { +#if defined(_MSC_VER) && (_MSC_VER < 1900) + pair() : month_day_(date::jan / 1), weekday_(0U) {} +#endif date::month_day month_day_; date::weekday weekday_; }; @@ -40,17 +43,37 @@ private: enum Type {month_day, month_last_dow, lteq, gteq}; Type type_{month_day}; + +#if defined(_MSC_VER) && (_MSC_VER < 1900) + struct U +#else union U +#endif { date::month_day month_day_; date::month_weekday_last month_weekday_last_; pair month_day_weekday_; +#if !defined(_MSC_VER) && (_MSC_VER >= 1900) U() : month_day_{date::jan/1} {} +#else + U() : + month_day_(date::jan/1), + month_weekday_last_(date::month(0U), date::weekday_last(date::weekday(0U))) + {} + + U(const date::month_day& month_day, + const date::weekday& weekday) : + month_day_(month_day), + month_weekday_last_(date::month(0U), date::weekday_last(weekday)) + {} +#endif // !defined(_MSC_VER) && (_MSC_VER >= 1900) + U& operator=(const date::month_day& x); U& operator=(const date::month_weekday_last& x); U& operator=(const pair& x); } u; + std::chrono::hours h_{0}; std::chrono::minutes m_{0}; std::chrono::seconds s_{0}; @@ -165,7 +188,12 @@ struct Zone::zonelet std::chrono::seconds gmtoff_; tag tag_ = has_rule; + +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) union U +#else + struct U +#endif { std::string rule_; std::chrono::minutes save_; @@ -175,6 +203,7 @@ struct Zone::zonelet U(const U&) {} U& operator=(const U&) = delete; } u; + std::string format_; date::year until_year_{0}; MonthDayTime until_date_;