forked from HowardHinnant/date
Add range checking for the time-of-day fields during parse.
* Also refactor the leap-second logic used in utc_time.
This commit is contained in:
43
date.h
43
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 <class CharT, class Traits>
|
||||
friend
|
||||
std::basic_ostream<CharT, Traits>&
|
||||
@@ -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 <class CharT, class Traits>
|
||||
friend
|
||||
std::basic_ostream<CharT, Traits>&
|
||||
@@ -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<class CharT, class Traits>
|
||||
friend
|
||||
std::basic_ostream<CharT, Traits>&
|
||||
@@ -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<class CharT, class Traits>
|
||||
friend
|
||||
std::basic_ostream<CharT, Traits>&
|
||||
@@ -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<class CharT, class Traits>
|
||||
friend
|
||||
std::basic_ostream<CharT, Traits>&
|
||||
@@ -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<class CharT, class Traits>
|
||||
friend
|
||||
std::basic_ostream<CharT, Traits>&
|
||||
@@ -6425,7 +6464,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
|
||||
auto offptr = offset ? offset : &offset_local;
|
||||
fields<CT> 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<Duration>(fds.tod.to_duration() - *offptr);
|
||||
@@ -6442,7 +6481,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
|
||||
using CT = typename common_type<Duration, seconds>::type;
|
||||
fields<CT> 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<Duration>(fds.tod.to_duration());
|
||||
|
85
tz.h
85
tz.h
@@ -1119,6 +1119,35 @@ to_utc_time(const sys_time<Duration>& st)
|
||||
return utc_time<duration>{st.time_since_epoch() + seconds{lt-leaps.begin()}};
|
||||
}
|
||||
|
||||
// Return pair<is_leap_second, seconds{number_of_leap_seconds_since_1970}>
|
||||
// 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 <class Duration>
|
||||
std::pair<bool, std::chrono::seconds>
|
||||
is_leap_second(date::utc_time<Duration> const& ut)
|
||||
{
|
||||
using namespace date;
|
||||
using namespace std::chrono;
|
||||
using duration = typename std::common_type<Duration, seconds>::type;
|
||||
auto const& leaps = get_tzdb().leaps;
|
||||
auto tp = sys_time<duration>{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 <class Duration>
|
||||
inline
|
||||
sys_time<typename std::common_type<Duration, std::chrono::seconds>::type>
|
||||
@@ -1127,19 +1156,10 @@ to_sys_time(const utc_time<Duration>& ut)
|
||||
using namespace std::chrono;
|
||||
using duration = typename std::common_type<Duration, seconds>::type;
|
||||
auto const& leaps = get_tzdb().leaps;
|
||||
auto tp = sys_time<duration>{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<duration>{ut.time_since_epoch() - ls.second};
|
||||
if (ls.first)
|
||||
tp = floor<seconds>(tp) + seconds{1} - duration{1};
|
||||
return tp;
|
||||
}
|
||||
|
||||
@@ -1162,26 +1182,14 @@ to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt,
|
||||
const string abbrev("UTC");
|
||||
CONSTDATA seconds offset{0};
|
||||
auto const& leaps = get_tzdb().leaps;
|
||||
auto tp = sys_time<CT>{t.time_since_epoch()};
|
||||
year_month_day ymd;
|
||||
time_of_day<CT> 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<CT>{t.time_since_epoch() - ls.second};
|
||||
auto const sd = floor<days>(tp);
|
||||
ymd = sd;
|
||||
time = make_time(tp - sd);
|
||||
time.seconds() += ls;
|
||||
time.seconds() += seconds{ls.first};
|
||||
fields<CT> fds{ymd, time};
|
||||
to_stream(os, fmt, fds, &abbrev, &offset);
|
||||
}
|
||||
@@ -1211,13 +1219,18 @@ from_stream(std::basic_istream<CharT, Traits>& 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<Duration>(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<Duration>(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1319,7 +1332,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
|
||||
auto offptr = offset ? offset : &offset_local;
|
||||
fields<CT> 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>{duration_cast<Duration>(
|
||||
@@ -1425,7 +1438,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
|
||||
auto offptr = offset ? offset : &offset_local;
|
||||
fields<CT> 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>{duration_cast<Duration>(
|
||||
|
Reference in New Issue
Block a user