Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 28 additions & 30 deletions stl/inc/xloctime
Original file line number Diff line number Diff line change
Expand Up @@ -93,37 +93,38 @@ public:
_State = ios_base::goodbit;

for (; _Fmtfirst != _Fmtlast; ++_Fmtfirst) {
if (_Ctype_fac.narrow(*_Fmtfirst) != '%') { // match literal element
if (_State != ios_base::goodbit) {
// N4878 [locale.time.get.members]/8.2
// _State is fail, eof, or bad. Do not proceed to the next fields. Return with current _State.
break;
} else if (_First == _Last) {
// N4878 [locale.time.get.members]/8.3
_State = ios_base::eofbit | ios_base::failbit;
break;
} else if (_Ctype_fac.narrow(*_Fmtfirst) != '%') { // match literal element
if (_Ctype_fac.is(_Ctype::space, *_Fmtfirst)) {
while (_First != _Last && _Ctype_fac.is(_Ctype::space, *_First)) {
++_First;
}
} else if (*_First != *_Fmtfirst) { // bad literal match
} else if (_Ctype_fac.tolower(*_First) != _Ctype_fac.tolower(*_Fmtfirst)) { // bad literal match
_State |= ios_base::failbit;
break;
} else {
++_First;
}
} else if (++_Fmtfirst == _Fmtlast) { // treat trailing % as literal match
if (*_First != _Fmtfirst[-1]) {
_State |= ios_base::failbit;
} else {
++_First;
}
} else if (++_Fmtfirst == _Fmtlast) {
// N4878 [locale.time.get.members]/8.4: "If the number of elements in the range [fmt, fmtend) is not
// sufficient to unambiguously determine whether the conversion specification is complete and valid,
// the function evaluates err = ios_base::failbit."
_State = ios_base::failbit;
break;
} else { // get specifier after %
char _Specifier = _Ctype_fac.narrow(*_Fmtfirst);
char _Modifier = '\0';
_Elem _Percent = _Fmtfirst[-1];

if (_Specifier == 'E' || _Specifier == 'O' || _Specifier == 'Q' || _Specifier == '#') {
if (++_Fmtfirst == _Fmtlast) { // no specifier, treat %[E0Q#] as literal match
if (*_First != _Percent || ++_First == _Last || _Ctype_fac.narrow(*_First) != _Specifier) {
_State |= ios_base::failbit;
} else {
++_First;
}

if (++_Fmtfirst == _Fmtlast) { // no specifier
_State = ios_base::failbit;
break;
} else { // save both qualifier and specifier
_Modifier = _Specifier;
Expand All @@ -132,18 +133,9 @@ public:
}

_First = do_get(_First, _Last, _Iosbase, _State, _Pt, _Specifier, _Modifier); // convert a single field

if (_State != ios_base::goodbit) {
// _State is fail, eof, or bad. Do not proceed to the next fields. Return with current _State.
break;
}
}
}

if (_First == _Last) {
_State |= ios_base::eofbit;
}

return _First;
}

Expand Down Expand Up @@ -223,7 +215,7 @@ protected:
if (_State != ios_base::goodbit || _Ctype_fac.narrow(*_First) != ':') {
_State |= ios_base::failbit; // min field is bad
} else {
_State |= _Getint(++_First, _Last, 0, 59, _Pt->tm_sec, _Ctype_fac);
_State |= _Getint(++_First, _Last, 0, 60, _Pt->tm_sec, _Ctype_fac);
}

return _First;
Expand Down Expand Up @@ -546,7 +538,14 @@ private:
char* _Ptr = _Ac;
char _Ch;

if (_First != _Last) {
int _Digits_seen = 0;

while (_First != _Last && _Digits_seen < _Hi_digits && _Ctype_fac.is(ctype_base::space, *_First)) {
++_First;
++_Digits_seen;
}

if (_First != _Last && _Digits_seen < _Hi_digits) {
if ((_Ch = _Ctype_fac.narrow(*_First)) == '+') { // copy plus sign
*_Ptr++ = '+';
++_First;
Expand All @@ -556,9 +555,8 @@ private:
}
}

int _Digits_seen = 0;

for (; _First != _Last && _Ctype_fac.narrow(*_First) == '0'; ++_First) { // strip leading zeros
for (; _First != _Last && _Digits_seen < _Hi_digits && _Ctype_fac.narrow(*_First) == '0';
++_First) { // strip leading zeros
++_Digits_seen;
}

Expand Down
58 changes: 58 additions & 0 deletions tests/std/tests/Dev11_0836436_get_time/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,64 @@ void test_990695() {
assert(t.tm_min == 8);
assert(t.tm_sec == 0);
}

{
// Should fail if EOF while not parsing specifier (N4878 [locale.time.get.members]/8.4).
tm t{};
istringstream iss("4");
iss >> get_time(&t, "42");
assert(iss.rdstate() == (ios_base::eofbit | ios_base::failbit));
}

{
// Trailing % should not be treated as a literal (N4878 [locale.time.get.members]/8.4 again).
tm t{};
istringstream iss("%");
iss >> get_time(&t, "%");
assert(iss.fail());
}

{
// % with modifier but no specifier is also incomplete.
tm t{};
istringstream iss("%E");
iss >> get_time(&t, "%E");
assert(iss.fail());
}

{
// Literal match is case-insensitive.
tm t{};
istringstream iss("aBc");
iss >> get_time(&t, "AbC");
assert(iss);
}

{
// GH-1606: reads too many leading zeros
istringstream iss("19700405T000006");
tm t{};
iss >> get_time(&t, "%Y%m%dT%H%M%S");
assert(iss);

printf("Expected hour 0, min 0, sec 6\n");
printf(" Got hour %d, min %d, sec %d\n", t.tm_hour, t.tm_min, t.tm_sec);

assert(t.tm_year == 70);
assert(t.tm_mon == 3);
assert(t.tm_mday == 5);
assert(t.tm_hour == 0);
assert(t.tm_min == 0);
assert(t.tm_sec == 6);
}

{
// strptime specification: "leading zeros are permitted but not required"
tm t{};
istringstream{" 7 4"} >> get_time(&t, "%m%d");
assert(t.tm_mon == 6);
assert(t.tm_mday == 4);
}
}
}

Expand Down