зеркало из https://github.com/microsoft/STL.git
`<xloctime>`: apply two-digit year logic only if exactly two digits are read (#2666)
Co-authored-by: Stephan T. Lavavej <stl@microsoft.com>
This commit is contained in:
Родитель
b0b9a58b35
Коммит
b965db4e56
153
stl/inc/xloctime
153
stl/inc/xloctime
|
@ -20,6 +20,72 @@ _STL_DISABLE_CLANG_WARNINGS
|
|||
#undef new
|
||||
|
||||
_STD_BEGIN
|
||||
template <class _InIt, class _Elem>
|
||||
ios_base::iostate _Getint_v2(_InIt& _First, _InIt& _Last, int _Lo, int _Hi, int& _Val, int& _Digits_read,
|
||||
const ctype<_Elem>& _Ctype_fac) { // get integer in range [_Lo, _Hi] from [_First, _Last)
|
||||
_STL_INTERNAL_CHECK(0 <= _Hi && _Hi <= 9999);
|
||||
const int _Hi_digits = (_Hi <= 9 ? 1 : _Hi <= 99 ? 2 : _Hi <= 999 ? 3 : 4);
|
||||
char _Ac[_MAX_INT_DIG];
|
||||
char* _Ep;
|
||||
char* _Ptr = _Ac;
|
||||
char _Ch;
|
||||
|
||||
_Digits_read = 0;
|
||||
|
||||
while (_First != _Last && _Digits_read < _Hi_digits && _Ctype_fac.is(ctype_base::space, *_First)) {
|
||||
++_First;
|
||||
++_Digits_read;
|
||||
}
|
||||
|
||||
if (_First != _Last && _Digits_read < _Hi_digits) {
|
||||
if ((_Ch = _Ctype_fac.narrow(*_First)) == '+') { // copy plus sign
|
||||
*_Ptr++ = '+';
|
||||
++_First;
|
||||
} else if (_Ch == '-') { // copy minus sign
|
||||
*_Ptr++ = '-';
|
||||
++_First;
|
||||
}
|
||||
}
|
||||
|
||||
for (; _First != _Last && _Digits_read < _Hi_digits && _Ctype_fac.narrow(*_First) == '0'; ++_First) {
|
||||
++_Digits_read; // strip leading zeros
|
||||
}
|
||||
|
||||
if (_Digits_read > 0) {
|
||||
*_Ptr++ = '0'; // replace one or more with single zero
|
||||
}
|
||||
|
||||
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) {
|
||||
++_Ptr; // drop trailing digits if already too large
|
||||
}
|
||||
}
|
||||
|
||||
if (_Digits_read == 0) {
|
||||
_Ptr = _Ac;
|
||||
}
|
||||
|
||||
*_Ptr = '\0';
|
||||
int _Errno = 0;
|
||||
const long _Ans = _CSTD _Stolx(_Ac, &_Ep, 10, &_Errno);
|
||||
ios_base::iostate _State = ios_base::goodbit;
|
||||
|
||||
if (_First == _Last) {
|
||||
_State |= ios_base::eofbit;
|
||||
}
|
||||
|
||||
if (_Ep == _Ac || _Errno != 0 || _Ans < _Lo || _Hi < _Ans) {
|
||||
_State |= ios_base::failbit; // bad conversion
|
||||
} else {
|
||||
_Val = _Ans; // store valid result
|
||||
}
|
||||
|
||||
return _State;
|
||||
}
|
||||
|
||||
struct _CRTIMP2_PURE_IMPORT time_base : locale::facet { // base class for time_get
|
||||
enum dateorder { // constants for different orders of date components
|
||||
no_order,
|
||||
|
@ -343,17 +409,20 @@ protected:
|
|||
ios_base::iostate& _State, tm* _Pt) const { // get year from [_First, _Last) into _Pt
|
||||
const _Ctype& _Ctype_fac = _STD use_facet<_Ctype>(_Iosbase.getloc());
|
||||
|
||||
int _Ans = 0;
|
||||
ios_base::iostate _Res = _Getint(_First, _Last, 0, 9999, _Ans, _Ctype_fac);
|
||||
int _Ans = 0;
|
||||
int _Digits_read;
|
||||
ios_base::iostate _Res = _Getint_v2(_First, _Last, 0, 9999, _Ans, _Digits_read, _Ctype_fac);
|
||||
|
||||
_State |= _Res; // pass on eofbit and failbit
|
||||
if (!(_Res & ios_base::failbit)) {
|
||||
if (_Ans < 69) {
|
||||
_Pt->tm_year = _Ans + 100; // [0, 68] parsed as [2000, 2068]
|
||||
} else if (_Ans < 100) {
|
||||
_Pt->tm_year = _Ans; // [69, 99] parsed as [1969, 1999]
|
||||
if (_Digits_read <= 2) {
|
||||
if (_Ans < 69) {
|
||||
_Pt->tm_year = _Ans + 100; // [00, 68] parsed as [2000, 2068]
|
||||
} else if (_Ans < 100) {
|
||||
_Pt->tm_year = _Ans; // [69, 99] parsed as [1969, 1999]
|
||||
}
|
||||
} else {
|
||||
_Pt->tm_year = _Ans - 1900; // [100, 9999] parsed literally
|
||||
_Pt->tm_year = _Ans - 1900; // parsed literally
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -485,7 +554,10 @@ protected:
|
|||
break;
|
||||
|
||||
case 'Y':
|
||||
_First = get_year(_First, _Last, _Iosbase, _State, _Pt);
|
||||
_State |= _Getint(_First, _Last, 0, 9999, _Ans, _Ctype_fac);
|
||||
if (!(_State & ios_base::failbit)) {
|
||||
_Pt->tm_year = _Ans - 1900;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -529,68 +601,9 @@ protected:
|
|||
private:
|
||||
ios_base::iostate __CLRCALL_OR_CDECL _Getint(_InIt& _First, _InIt& _Last, int _Lo, int _Hi, int& _Val,
|
||||
const _Ctype& _Ctype_fac) const { // get integer in range [_Lo, _Hi] from [_First, _Last)
|
||||
_STL_INTERNAL_CHECK(0 <= _Hi && _Hi <= 9999);
|
||||
const int _Hi_digits = (_Hi <= 9 ? 1 : _Hi <= 99 ? 2 : _Hi <= 999 ? 3 : 4);
|
||||
char _Ac[_MAX_INT_DIG];
|
||||
char* _Ep;
|
||||
char* _Ptr = _Ac;
|
||||
char _Ch;
|
||||
|
||||
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;
|
||||
} else if (_Ch == '-') { // copy minus sign
|
||||
*_Ptr++ = '-';
|
||||
++_First;
|
||||
}
|
||||
}
|
||||
|
||||
for (; _First != _Last && _Digits_seen < _Hi_digits && _Ctype_fac.narrow(*_First) == '0';
|
||||
++_First) { // strip leading zeros
|
||||
++_Digits_seen;
|
||||
}
|
||||
|
||||
if (_Digits_seen > 0) {
|
||||
*_Ptr++ = '0'; // replace one or more with single zero
|
||||
}
|
||||
|
||||
for (char* const _Pe = &_Ac[_MAX_INT_DIG - 1];
|
||||
_First != _Last && '0' <= (_Ch = _Ctype_fac.narrow(*_First)) && _Ch <= '9' && _Digits_seen < _Hi_digits;
|
||||
++_Digits_seen, (void) ++_First) { // copy digits
|
||||
*_Ptr = _Ch;
|
||||
if (_Ptr < _Pe) {
|
||||
++_Ptr; // drop trailing digits if already too large
|
||||
}
|
||||
}
|
||||
|
||||
if (_Digits_seen == 0) {
|
||||
_Ptr = _Ac;
|
||||
}
|
||||
|
||||
*_Ptr = '\0';
|
||||
int _Errno = 0;
|
||||
const long _Ans = _CSTD _Stolx(_Ac, &_Ep, 10, &_Errno);
|
||||
ios_base::iostate _State = ios_base::goodbit;
|
||||
|
||||
if (_First == _Last) {
|
||||
_State |= ios_base::eofbit;
|
||||
}
|
||||
|
||||
if (_Ep == _Ac || _Errno != 0 || _Ans < _Lo || _Hi < _Ans) {
|
||||
_State |= ios_base::failbit; // bad conversion
|
||||
} else {
|
||||
_Val = _Ans; // store valid result
|
||||
}
|
||||
|
||||
return _State;
|
||||
// TRANSITION, ABI
|
||||
int _Digits_read;
|
||||
return _Getint_v2(_First, _Last, _Lo, _Hi, _Val, _Digits_read, _Ctype_fac);
|
||||
}
|
||||
|
||||
void __CLR_OR_THIS_CALL _Tidy() noexcept { // free all storage
|
||||
|
|
|
@ -18,6 +18,7 @@ using namespace std;
|
|||
// DevDiv-821672 "<locale>: visual studio.net 2013 time libraries buggy (%x %X) - time_get"
|
||||
// DevDiv-836436 "<iomanip>: get_time()'s AM/PM parsing is broken"
|
||||
// DevDiv-872926 "<locale>: time_get::get parsing format string gets tm::tm_hour wrong [libcxx]"
|
||||
// VSO-1259138/GH-2618 "<xloctime>: get_time does not return correct year in tm.tm_year if year is 1"
|
||||
|
||||
tm helper(const char* const s, const char* const fmt) {
|
||||
tm t{};
|
||||
|
@ -107,6 +108,7 @@ void test_locale_german();
|
|||
void test_locale_chinese();
|
||||
void test_invalid_argument();
|
||||
void test_buffer_resizing();
|
||||
void test_gh_2618();
|
||||
|
||||
int main() {
|
||||
assert(read_hour("12 AM") == 0);
|
||||
|
@ -152,6 +154,7 @@ int main() {
|
|||
test_locale_chinese();
|
||||
test_invalid_argument();
|
||||
test_buffer_resizing();
|
||||
test_gh_2618();
|
||||
}
|
||||
|
||||
typedef istreambuf_iterator<char> Iter;
|
||||
|
@ -821,3 +824,51 @@ void test_buffer_resizing() {
|
|||
assert(ss.rdstate() == ios_base::goodbit);
|
||||
}
|
||||
}
|
||||
|
||||
void test_gh_2618() {
|
||||
auto TestTimeGetYear = [](const char* input, const int expected_y, const int expected_Y,
|
||||
const int expected_get_year) {
|
||||
{
|
||||
tm time{};
|
||||
istringstream iss{input};
|
||||
iss >> get_time(&time, "%y");
|
||||
assert(time.tm_year + 1900 == expected_y);
|
||||
}
|
||||
|
||||
{
|
||||
tm time{};
|
||||
istringstream iss{input};
|
||||
iss >> get_time(&time, "%Y");
|
||||
assert(time.tm_year + 1900 == expected_Y);
|
||||
}
|
||||
|
||||
{
|
||||
tm time{};
|
||||
ios_base::iostate state{};
|
||||
istringstream iss{input};
|
||||
use_facet<time_get<char>>(iss.getloc()).get_year({iss}, {}, iss, state, &time);
|
||||
assert(time.tm_year + 1900 == expected_get_year);
|
||||
}
|
||||
};
|
||||
|
||||
// 4-digit strings: 'y' should only read the first two digits, 'Y' and `get_year` should agree
|
||||
TestTimeGetYear("0001", 2000, 1, 1);
|
||||
TestTimeGetYear("0080", 2000, 80, 80);
|
||||
TestTimeGetYear("1995", 2019, 1995, 1995);
|
||||
TestTimeGetYear("2022", 2020, 2022, 2022);
|
||||
TestTimeGetYear("8522", 1985, 8522, 8522);
|
||||
|
||||
// 3-digit strings: same as 4-digit
|
||||
TestTimeGetYear("001", 2000, 1, 1);
|
||||
TestTimeGetYear("080", 2008, 80, 80);
|
||||
TestTimeGetYear("995", 1999, 995, 995);
|
||||
|
||||
// 2-digit strings: 'Y' should parse literally, `get_year` should behave as 'y'
|
||||
TestTimeGetYear("01", 2001, 1, 2001);
|
||||
TestTimeGetYear("80", 1980, 80, 1980);
|
||||
TestTimeGetYear("95", 1995, 95, 1995);
|
||||
TestTimeGetYear("22", 2022, 22, 2022);
|
||||
|
||||
// 1-digit strings: same as 2-digit
|
||||
TestTimeGetYear("1", 2001, 1, 2001);
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче