Skip to content
Open
4 changes: 2 additions & 2 deletions stl/inc/chrono
Original file line number Diff line number Diff line change
Expand Up @@ -4212,8 +4212,8 @@ namespace chrono {
char _Ch;
while (_First != _Last && _Width > 0 && '0' <= (_Ch = _Ctype_fac.narrow(*_First))
&& _Ch <= '9') { // copy digits
*_Ptr = _Ch;
if (_Ptr < _STD cend(_Ac)) {
if (_Ptr < _STD cend(_Ac) - 1) { // preserve room for the null terminator written below
*_Ptr = _Ch;
++_Ptr; // drop trailing digits if already too large
}
Comment thread
StephanTLavavej marked this conversation as resolved.
++_First;
Expand Down
4 changes: 2 additions & 2 deletions stl/inc/xloctime
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ ios_base::iostate _Getint_v2(_InIt& _First, _InIt& _Last, int _Lo, int _Hi, int&
for (char* const _Pe = &_Ac[_Max_int_dig - 1];
_First != _Last && '0' <= (_Ch = _Ctype_fac.narrow(*_First)) && _Ch <= '9' && _Digits_read < _Hi_digits;
++_Digits_read, (void) ++_First) { // copy digits
*_Ptr = _Ch;
if (_Ptr < _Pe) {
if (_Ptr < _Pe) { // preserve room for the null terminator written below
*_Ptr = _Ch;
++_Ptr; // drop trailing digits if already too large
}
}
Expand Down
278 changes: 244 additions & 34 deletions tests/std/tests/P0355R7_calendars_and_time_zones_io/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@
#include <charconv>
#include <chrono>
#include <cstdint>
#include <cstdio>
#include <istream>
#include <iterator>
#include <limits>
#include <locale>
#include <ratio>
#include <source_location>
#include <sstream>
#include <string>
#include <type_traits>
Expand All @@ -36,7 +38,7 @@ bool test_duration_basic_out(const duration<Rep, Period>& d, const CharT* expect

template <class CharT>
bool test_duration_locale_out() {
basic_stringstream<CharT> ss;
basic_ostringstream<CharT> ss;
const duration<double> d{0.140625};
ss.precision(3);
ss << d;
Expand Down Expand Up @@ -153,7 +155,7 @@ ios_base::iostate parse_state(const CharT* str, const CStringOrStdString& fmt, P
*offset = minutes::min();
}

basic_stringstream<CharT> sstr{str};
basic_istringstream<CharT> sstr{str};
if (abbrev) {
if (offset) {
sstr >> parse(fmt, p, *abbrev, *offset);
Expand All @@ -173,14 +175,40 @@ ios_base::iostate parse_state(const CharT* str, const CStringOrStdString& fmt, P

template <class CharT, class CStringOrStdString, class Parsable>
void test_parse(const CharT* str, const CStringOrStdString& fmt, Parsable& p,
type_identity_t<basic_string<CharT>*> abbrev = nullptr, minutes* offset = nullptr) {
assert((parse_state(str, fmt, p, abbrev, offset) & ~ios_base::eofbit) == ios_base::goodbit);
type_identity_t<basic_string<CharT>*> abbrev = nullptr, minutes* offset = nullptr,
const source_location sl = source_location::current()) {
const auto masked_state = parse_state(str, fmt, p, abbrev, offset) & ~ios_base::eofbit;
const bool desirable = masked_state == ios_base::goodbit;
if (!desirable) {
printf("test_parse() encountered a problem on line %u.\n", static_cast<unsigned int>(sl.line()));
}
assert(desirable);
Comment thread
StephanTLavavej marked this conversation as resolved.
}

template <class CharT, class CStringOrStdString, class Parsable>
void test_parse(const basic_string<CharT>& str, const CStringOrStdString& fmt, Parsable& p,
type_identity_t<basic_string<CharT>*> abbrev = nullptr, minutes* offset = nullptr,
const source_location sl = source_location::current()) {
test_parse(str.c_str(), fmt, p, abbrev, offset, sl);
}

template <class CharT, class CStringOrStdString, class Parsable>
void fail_parse(const CharT* str, const CStringOrStdString& fmt, Parsable& p,
type_identity_t<basic_string<CharT>*> abbrev = nullptr, minutes* offset = nullptr) {
assert((parse_state(str, fmt, p, abbrev, offset) & ~ios_base::eofbit) != ios_base::goodbit);
type_identity_t<basic_string<CharT>*> abbrev = nullptr, minutes* offset = nullptr,
const source_location sl = source_location::current()) {
const auto masked_state = parse_state(str, fmt, p, abbrev, offset) & ~ios_base::eofbit;
const bool desirable = masked_state != ios_base::goodbit;
if (!desirable) {
printf("fail_parse() encountered a problem on line %u.\n", static_cast<unsigned int>(sl.line()));
}
assert(desirable);
Comment thread
StephanTLavavej marked this conversation as resolved.
}

template <class CharT, class CStringOrStdString, class Parsable>
void fail_parse(const basic_string<CharT>& str, const CStringOrStdString& fmt, Parsable& p,
type_identity_t<basic_string<CharT>*> abbrev = nullptr, minutes* offset = nullptr,
const source_location sl = source_location::current()) {
fail_parse(str.c_str(), fmt, p, abbrev, offset, sl);
}

template <class TimeType, class IntType = int>
Expand Down Expand Up @@ -208,29 +236,6 @@ void test_limits(const char* flag, const IntType min, const IntType max) {
assert(value == TimeType{max});
}

void test_lwg_3536() {
// LWG-3536, "Should chrono::from_stream() assign zero to duration for failure?"
minutes mm{20};

{
istringstream iss{"2:2:30"};
iss >> parse("%H:%M:%S", mm);
assert(iss.fail() && mm == 20min);
}

{
istringstream iss{"June"};
iss >> parse("%B", mm);
assert(iss.fail() && mm == 20min);
}

{
istringstream iss{""};
iss >> parse("%B", mm);
assert(iss.fail() && mm == 20min);
}
}

void parse_seconds() {
seconds time;
test_parse("1", "%S", time);
Expand Down Expand Up @@ -631,7 +636,7 @@ void parse_calendar_types_basic() {
// not ambiguous with year
year_month_day ymd;
test_parse("60 2004-02-29", "%j %F", ymd);
assert(ymd == 2004y / February / 29);
assert(ymd == 2004y / February / 29d);

// basic year_month_day tests
// different ways of specifying year
Expand Down Expand Up @@ -1221,6 +1226,187 @@ void parse_timepoints() {
assert(ut == clock_cast<utc_clock>(sys_days{1d / January / 2017y}) - 1s);
}

void parse_modified_maximum_characters() {
const string hundred_digits(100, '1');
const string seventy_zeroes(70, '0');

{
day d{7};
test_parse(hundred_digits, "%d", d);
assert(d == day{11});
fail_parse(seventy_zeroes + "22", "%d", d); // 0 is not a valid day of the month
fail_parse(hundred_digits, "%100d", d); // overflow
test_parse(seventy_zeroes + "22", "%100d", d);
assert(d == day{22});

test_parse(hundred_digits, "%e", d);
assert(d == day{11});
fail_parse(seventy_zeroes + "22", "%e", d); // 0 is not a valid day of the month
fail_parse(hundred_digits, "%100e", d); // overflow
test_parse(seventy_zeroes + "22", "%100e", d);
assert(d == day{22});
}

{
hours h{33h};
test_parse(hundred_digits, "%H", h);
assert(h == 11h);
test_parse(seventy_zeroes + "22", "%H", h);
assert(h == 0h);
fail_parse(hundred_digits, "%100H", h); // overflow
test_parse(seventy_zeroes + "22", "%100H", h);
assert(h == 22h);

test_parse(hundred_digits, "%I", h);
assert(h == 11h);
fail_parse(seventy_zeroes + "7", "%I", h); // 0 is not a valid 12-hour clock
fail_parse(hundred_digits, "%100I", h); // overflow
test_parse(seventy_zeroes + "7", "%100I", h);
assert(h == 7h);
}

{
days ds{22};
test_parse(hundred_digits, "%j", ds);
assert(ds == days{111});
test_parse(seventy_zeroes + "1729", "%j", ds);
assert(ds == days{0});
fail_parse(hundred_digits, "%100j", ds); // overflow
test_parse(seventy_zeroes + "1729", "%100j", ds);
assert(ds == days{1729});
Comment thread
StephanTLavavej marked this conversation as resolved.
}

{
year_month_day ymd{1970y / January / 1d};
test_parse("2030 " + hundred_digits, "%Y %j", ymd);
assert(ymd == 2030y / April / 21d);
fail_parse("2030 " + seventy_zeroes + "124", "%Y %j", ymd); // 0 is not a valid day of the year
fail_parse("2030 " + hundred_digits, "%Y %100j", ymd); // overflow
test_parse("2030 " + seventy_zeroes + "124", "%Y %100j", ymd);
assert(ymd == 2030y / May / 4d);

fail_parse(hundred_digits + "-02-03", "%100F", ymd); // overflow
test_parse(seventy_zeroes + "1969-07-20", "%100F", ymd);
assert(ymd == 1969y / July / 20d);

test_parse("2015 4 " + hundred_digits, "%Y %u %U", ymd);
assert(ymd == 2015y / March / 19d);
test_parse("2015 4 " + seventy_zeroes + "6", "%Y %u %U", ymd);
assert(ymd == 2015y / January / 1d);
fail_parse("2015 4 " + hundred_digits, "%Y %u %100U", ymd); // overflow
test_parse("2015 4 " + seventy_zeroes + "6", "%Y %u %100U", ymd);
assert(ymd == 2015y / February / 12d);

test_parse("2015 4 " + hundred_digits, "%Y %u %W", ymd);
assert(ymd == 2015y / March / 19d);
test_parse("2015 4 " + seventy_zeroes + "6", "%Y %u %W", ymd);
assert(ymd == 2015y / January / 1d);
fail_parse("2015 4 " + hundred_digits, "%Y %u %100W", ymd); // overflow
test_parse("2015 4 " + seventy_zeroes + "6", "%Y %u %100W", ymd);
assert(ymd == 2015y / February / 12d);

test_parse("4 03 19 " + hundred_digits, "%u %V %C %g", ymd);
assert(ymd == 1911y / January / 19d);
test_parse("4 03 19 " + seventy_zeroes + "33", "%u %V %C %g", ymd);
assert(ymd == 1900y / January / 18d);
fail_parse("4 03 19 " + hundred_digits, "%u %V %C %100g", ymd); // overflow
test_parse("4 03 19 " + seventy_zeroes + "55", "%u %V %C %100g", ymd);
assert(ymd == 1955y / January / 20d);

test_parse("4 03 " + hundred_digits, "%u %V %G", ymd);
assert(ymd == 1111y / January / 19d);
test_parse("4 03 " + seventy_zeroes + "2030", "%u %V %G", ymd);
assert(ymd == 0y / January / 20d);
fail_parse("4 03 " + hundred_digits, "%u %V %100G", ymd); // overflow
test_parse("4 03 " + seventy_zeroes + "2030", "%u %V %100G", ymd);
assert(ymd == 2030y / January / 17d);

test_parse("4 2015 " + hundred_digits, "%u %G %V", ymd);
assert(ymd == 2015y / March / 12d);
fail_parse("4 2015 " + seventy_zeroes + "33", "%u %G %V", ymd); // 0 is not a valid ISO week
fail_parse("4 2015 " + hundred_digits, "%u %G %100V", ymd); // overflow
test_parse("4 2015 " + seventy_zeroes + "33", "%u %G %100V", ymd);
assert(ymd == 2015y / August / 13d);
}

{
month mo{January};
test_parse(hundred_digits, "%m", mo);
assert(mo == November);
fail_parse(seventy_zeroes + "12", "%m", mo); // 0 is not a valid month
fail_parse(hundred_digits, "%100m", mo); // overflow
test_parse(seventy_zeroes + "12", "%100m", mo);
assert(mo == December);
}

{
minutes mi{22min};
test_parse(hundred_digits, "%M", mi);
assert(mi == 11min);
test_parse(seventy_zeroes + "55", "%M", mi);
assert(mi == 0min);
fail_parse(hundred_digits, "%100M", mi); // overflow
test_parse(seventy_zeroes + "55", "%100M", mi);
assert(mi == 55min);
}

{
seconds s{22s};
test_parse(hundred_digits, "%S", s);
assert(s == 11s);
test_parse(seventy_zeroes + "55", "%S", s);
assert(s == 0s);
fail_parse(hundred_digits, "%100S", s); // overflow
test_parse(seventy_zeroes + "55", "%100S", s);
assert(s == 55s);
}

{
weekday wd{Thursday};
test_parse(hundred_digits, "%u", wd);
assert(wd == Monday);
fail_parse(seventy_zeroes + "2", "%u", wd); // 0 is not a valid weekday for %u
fail_parse(hundred_digits, "%100u", wd); // overflow
test_parse(seventy_zeroes + "2", "%100u", wd);
assert(wd == Tuesday);

test_parse(hundred_digits, "%w", wd);
assert(wd == Monday);
test_parse(seventy_zeroes + "2", "%w", wd);
assert(wd == Sunday);
fail_parse(hundred_digits, "%100w", wd); // overflow
test_parse(seventy_zeroes + "2", "%100w", wd);
assert(wd == Tuesday);
}

{
year y{1970y};
test_parse(hundred_digits, "%y", y);
assert(y == 2011y);
test_parse(seventy_zeroes + "55", "%y", y);
assert(y == 2000y);
fail_parse(hundred_digits, "%100y", y); // overflow
test_parse(seventy_zeroes + "55", "%100y", y);
assert(y == 2055y);

test_parse(hundred_digits, "%Y", y);
assert(y == 1111y);
test_parse(seventy_zeroes + "2026", "%Y", y);
assert(y == 0y);
fail_parse(hundred_digits, "%100Y", y); // overflow
test_parse(seventy_zeroes + "2026", "%100Y", y);
assert(y == 2026y);

test_parse("33 " + hundred_digits, "%y %C", y);
assert(y == 1133y);
test_parse("33 " + seventy_zeroes + "22", "%y %C", y);
assert(y == 33y);
fail_parse("33 " + hundred_digits, "%y %100C", y); // overflow
test_parse("33 " + seventy_zeroes + "22", "%y %100C", y);
assert(y == 2233y);
}
}

template <class CharT, class CStringOrStdString>
void test_io_manipulator() {
seconds time;
Expand Down Expand Up @@ -1254,6 +1440,29 @@ void test_io_manipulator() {
fail_parse(WIDEN(CharT, "a b"), CStringOrStdString{WIDEN(CharT, "a%nb")}, time);
}

void test_lwg_3536() {
// LWG-3536, "Should chrono::from_stream() assign zero to duration for failure?"
minutes mm{20};

{
istringstream iss{"2:2:30"};
iss >> parse("%H:%M:%S", mm);
assert(iss.fail() && mm == 20min);
}

{
istringstream iss{"June"};
iss >> parse("%B", mm);
assert(iss.fail() && mm == 20min);
}

{
istringstream iss{""};
iss >> parse("%B", mm);
assert(iss.fail() && mm == 20min);
}
}

namespace lwg_3956 {
struct has_adl_from_stream {
int value = 0;
Expand Down Expand Up @@ -1297,9 +1506,7 @@ void test_lwg_3956() {
}
}

void test_parse() {
test_lwg_3536();
test_lwg_3956();
void test_all_parse() {
parse_seconds();
parse_minutes();
parse_hours();
Expand All @@ -1311,15 +1518,18 @@ void test_parse() {
parse_incomplete();
parse_whitespace();
parse_timepoints();
parse_modified_maximum_characters();
test_io_manipulator<char, const char*>();
test_io_manipulator<wchar_t, const wchar_t*>();
test_io_manipulator<char, string>();
test_io_manipulator<wchar_t, wstring>();
test_lwg_3536();
test_lwg_3956();
}

void test() {
test_duration_output();
test_parse();
test_all_parse();
}

int main() {
Expand Down