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:
Howard Hinnant
2017-04-07 18:52:09 -04:00
parent a4eef8e20c
commit 4f27361378
2 changed files with 90 additions and 38 deletions

43
date.h
View File

@@ -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
View File

@@ -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>(