diff --git a/date.h b/date.h index 9a1bdaf..54b9668 100644 --- a/date.h +++ b/date.h @@ -3494,6 +3494,12 @@ public: return s_ + sub_s_; } + CONSTCD11 bool in_conventional_range() const NOEXCEPT + { + using namespace std::chrono; + return sub_s_ < std::chrono::seconds{1} && s_ < minutes{1}; + } + template friend std::basic_ostream& @@ -3532,6 +3538,12 @@ public: CONSTCD11 std::chrono::seconds seconds() const NOEXCEPT {return s_;} CONSTCD14 precision to_duration() const NOEXCEPT {return s_;} + CONSTCD11 bool in_conventional_range() const NOEXCEPT + { + using namespace std::chrono; + return s_ < minutes{1}; + } + template friend std::basic_ostream& @@ -3622,6 +3634,11 @@ protected: CONSTCD14 void make12() NOEXCEPT; CONSTCD14 std::chrono::hours to24hr() const; + + CONSTCD11 bool in_conventional_range() const NOEXCEPT + { + return !neg_ && h_ < days{1}; + } }; CONSTCD14 @@ -3717,6 +3734,11 @@ public: CONSTCD14 time_of_day_storage& make24() NOEXCEPT {base::make24(); return *this;} CONSTCD14 time_of_day_storage& make12() NOEXCEPT {base::make12(); return *this;} + CONSTCD11 bool in_conventional_range() const NOEXCEPT + { + return base::in_conventional_range(); + } + template friend std::basic_ostream& @@ -3795,6 +3817,11 @@ public: CONSTCD14 time_of_day_storage& make24() NOEXCEPT {base::make24(); return *this;} CONSTCD14 time_of_day_storage& make12() NOEXCEPT {base::make12(); return *this;} + CONSTCD11 bool in_conventional_range() const NOEXCEPT + { + return base::in_conventional_range() && m_ < std::chrono::hours{1}; + } + template friend std::basic_ostream& @@ -3879,6 +3906,12 @@ public: CONSTCD14 time_of_day_storage& make24() NOEXCEPT {base::make24(); return *this;} CONSTCD14 time_of_day_storage& make12() NOEXCEPT {base::make12(); return *this;} + CONSTCD11 bool in_conventional_range() const NOEXCEPT + { + return base::in_conventional_range() && m_ < std::chrono::hours{1} && + s_.in_conventional_range(); + } + template friend std::basic_ostream& @@ -3983,6 +4016,12 @@ public: CONSTCD14 time_of_day_storage& make24() NOEXCEPT {base::make24(); return *this;} CONSTCD14 time_of_day_storage& make12() NOEXCEPT {base::make12(); return *this;} + CONSTCD11 bool in_conventional_range() const NOEXCEPT + { + return base::in_conventional_range() && m_ < std::chrono::hours{1} && + s_.in_conventional_range(); + } + template friend std::basic_ostream& @@ -6425,7 +6464,7 @@ from_stream(std::basic_istream& is, const CharT* fmt, auto offptr = offset ? offset : &offset_local; fields fds{}; from_stream(is, fmt, fds, abbrev, offptr); - if (!fds.ymd.ok()) + if (!fds.ymd.ok() || !fds.tod.in_conventional_range()) is.setstate(ios::failbit); if (!is.fail()) tp = sys_days(fds.ymd) + duration_cast(fds.tod.to_duration() - *offptr); @@ -6442,7 +6481,7 @@ from_stream(std::basic_istream& is, const CharT* fmt, using CT = typename common_type::type; fields fds{}; from_stream(is, fmt, fds, abbrev, offset); - if (!fds.ymd.ok()) + if (!fds.ymd.ok() || !fds.tod.in_conventional_range()) is.setstate(ios::failbit); if (!is.fail()) tp = local_days(fds.ymd) + duration_cast(fds.tod.to_duration()); diff --git a/tz.h b/tz.h index 8c9966d..0f09350 100644 --- a/tz.h +++ b/tz.h @@ -1119,6 +1119,35 @@ to_utc_time(const sys_time& 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> @@ -1127,19 +1156,10 @@ to_sys_time(const utc_time& ut) 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()}; - 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}; - } - } + 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; } @@ -1162,26 +1182,14 @@ to_stream(std::basic_ostream& os, const CharT* fmt, const string abbrev("UTC"); CONSTDATA seconds offset{0}; auto const& leaps = get_tzdb().leaps; - auto tp = sys_time{t.time_since_epoch()}; year_month_day ymd; time_of_day time; - seconds ls{0}; - 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}) - ls = seconds{1}; - else - tp += seconds{1}; - } - } + auto ls = is_leap_second(t); + auto tp = sys_time{t.time_since_epoch() - ls.second}; auto const sd = floor(tp); ymd = sd; time = make_time(tp - sd); - time.seconds() += ls; + time.seconds() += seconds{ls.first}; fields fds{ymd, time}; to_stream(os, fmt, fds, &abbrev, &offset); } @@ -1211,13 +1219,18 @@ from_stream(std::basic_istream& is, const CharT* fmt, is.setstate(ios::failbit); if (!is.fail()) { - bool is_leap_second = fds.tod.seconds() == seconds{60}; - if (is_leap_second) + bool is_60_sec = fds.tod.seconds() == seconds{60}; + if (is_60_sec) fds.tod.seconds() -= seconds{1}; - tp = to_utc_time(sys_days(fds.ymd) + - duration_cast(fds.tod.to_duration() - *offptr)); - if (is_leap_second) - tp += 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; + } + tp = time_point_cast(tmp); } } @@ -1319,7 +1332,7 @@ from_stream(std::basic_istream& is, const CharT* fmt, auto offptr = offset ? offset : &offset_local; fields fds{}; from_stream(is, fmt, fds, abbrev, offptr); - if (!fds.ymd.ok()) + if (!fds.ymd.ok() || !fds.tod.in_conventional_range()) is.setstate(ios::failbit); if (!is.fail()) tp = tai_time{duration_cast( @@ -1425,7 +1438,7 @@ from_stream(std::basic_istream& is, const CharT* fmt, auto offptr = offset ? offset : &offset_local; fields fds{}; from_stream(is, fmt, fds, abbrev, offptr); - if (!fds.ymd.ok()) + if (!fds.ymd.ok() || !fds.tod.in_conventional_range()) is.setstate(ios::failbit); if (!is.fail()) tp = gps_time{duration_cast(