diff --git a/stl/inc/xlocale b/stl/inc/xlocale index 5877d9aecb8..3f2a2a488de 100644 --- a/stl/inc/xlocale +++ b/stl/inc/xlocale @@ -461,49 +461,6 @@ const _Facet& __CRTDECL use_facet(const locale& _Loc) { // get facet reference f _END_LOCK() } // end of use_facet body -// FUNCTION TEMPLATE _Getloctxt -template -int __CRTDECL _Getloctxt(_InIt& _First, _InIt& _Last, size_t _Numfields, const _Elem* _Ptr) { - // find field at _Ptr that matches longest in [_First, _Last) - for (size_t _Off = 0; _Ptr[_Off] != _Elem{}; ++_Off) { - if (_Ptr[_Off] == _Ptr[0]) { - ++_Numfields; // add fields with leading mark to initial count - } - } - - string _Str(_Numfields, '\0'); // one column counter for each field - - int _Ans = -2; // no candidates so far - for (size_t _Column = 1;; ++_Column, (void) ++_First, _Ans = -1) { // test each element against all viable fields - bool _Prefix = false; // seen at least one valid prefix - size_t _Off = 0; // offset into fields - size_t _Field = 0; // current field number - - for (; _Field < _Numfields; ++_Field) { // test element at _Column in field _Field - while (_Ptr[_Off] != _Elem{} && _Ptr[_Off] != _Ptr[0]) { // find beginning of field - ++_Off; - } - - if (_Str[_Field] != '\0') { - _Off += _Str[_Field]; // skip tested columns in field - } else if (_Ptr[_Off += _Column] == _Ptr[0] - || _Ptr[_Off] == _Elem{}) { // matched all of field, save as possible answer - _Str[_Field] = static_cast(_Column < 127 ? _Column : 127); // save skip count if small enough - _Ans = static_cast(_Field); // save answer - } else if (_First == _Last || _Ptr[_Off] != *_First) { - _Str[_Field] = static_cast(_Column < 127 ? _Column : 127); // no match, just save skip count - } else { - _Prefix = true; // still a valid prefix - } - } - - if (!_Prefix || _First == _Last) { - break; // no pending prefixes or no input, give up - } - } - return _Ans; // return field number or negative value on failure -} - // FUNCTION TEMPLATE _Maklocbyte template char __CRTDECL _Maklocbyte(_Elem _Char, const _Locinfo::_Cvtvec&) { @@ -3263,6 +3220,50 @@ protected: virtual __CLR_OR_THIS_CALL ~ctype_byname() noexcept {} }; +// FUNCTION TEMPLATE _Getloctxt +template +int __CRTDECL _Getloctxt(_InIt& _First, _InIt& _Last, size_t _Numfields, const _Elem* _Ptr) { + // find field at _Ptr that matches longest in [_First, _Last) + for (size_t _Off = 0; _Ptr[_Off] != _Elem{}; ++_Off) { + if (_Ptr[_Off] == _Ptr[0]) { + ++_Numfields; // add fields with leading mark to initial count + } + } + + string _Str(_Numfields, '\0'); // one column counter for each field + const ctype<_Elem>& _CType = _STD use_facet>(locale{}); + + int _Ans = -2; // no candidates so far + for (size_t _Column = 1;; ++_Column, (void) ++_First, _Ans = -1) { // test each element against all viable fields + bool _Prefix = false; // seen at least one valid prefix + size_t _Off = 0; // offset into fields + size_t _Field = 0; // current field number + + for (; _Field < _Numfields; ++_Field) { // test element at _Column in field _Field + while (_Ptr[_Off] != _Elem{} && _Ptr[_Off] != _Ptr[0]) { // find beginning of field + ++_Off; + } + + if (_Str[_Field] != '\0') { + _Off += _Str[_Field]; // skip tested columns in field + } else if (_Ptr[_Off += _Column] == _Ptr[0] + || _Ptr[_Off] == _Elem{}) { // matched all of field, save as possible answer + _Str[_Field] = static_cast(_Column < 127 ? _Column : 127); // save skip count if small enough + _Ans = static_cast(_Field); // save answer + } else if (_First == _Last || _CType.tolower(_Ptr[_Off]) != _CType.tolower(static_cast<_Elem>(*_First))) { + _Str[_Field] = static_cast(_Column < 127 ? _Column : 127); // no match, just save skip count + } else { + _Prefix = true; // still a valid prefix + } + } + + if (!_Prefix || _First == _Last) { + break; // no pending prefixes or no input, give up + } + } + return _Ans; // return field number or negative value on failure +} + #if defined(_DLL_CPPLIB) #if !defined(_CRTBLD) || defined(__FORCE_INSTANCE) template class _CRTIMP2_PURE_IMPORT codecvt; diff --git a/stl/inc/xloctime b/stl/inc/xloctime index bf342ba6bd9..082bbd4e35e 100644 --- a/stl/inc/xloctime +++ b/stl/inc/xloctime @@ -132,6 +132,11 @@ public: } _First = do_get(_First, _Last, _Iosbase, _State, _Pt, _Specifier, _Modifier); // convert a single field + + if (_State != ios_base::goodbit) { + // Failed to convert the field. Do not proceed to the next fields. Return with failed _State. + break; + } } } diff --git a/tests/std/tests/Dev11_0836436_get_time/test.cpp b/tests/std/tests/Dev11_0836436_get_time/test.cpp index 78c69d18074..12b64dfc8ae 100644 --- a/tests/std/tests/Dev11_0836436_get_time/test.cpp +++ b/tests/std/tests/Dev11_0836436_get_time/test.cpp @@ -29,6 +29,18 @@ tm helper(const char* const s, const char* const fmt) { return t; } +tm helper_locale(const wchar_t* const s, const wchar_t* const fmt, const char* _loc) { + tm t{}; + + wstringstream wss(s); + wss.imbue(locale(_loc)); + wss >> get_time(&t, fmt); + + assert(static_cast(wss)); + + return t; +} + int read_hour(const char* const s) { const auto t = helper(s, "%I %p"); @@ -59,6 +71,17 @@ tuple read_date(const char* const s) { return make_tuple(t.tm_mday, t.tm_mon, t.tm_year); } +tuple read_date_locale(const wchar_t* const s, const char* _loc) { + const auto t = helper_locale(s, L"%Y-%b-%d", _loc); + + // int tm_mday; // day of the month - [1, 31] + // int tm_mon; // months since January - [0, 11] + // int tm_year; // years since 1900 + + return make_tuple(t.tm_mday, t.tm_mon, t.tm_year); +} + + tuple read_time(const char* const s) { const auto t = helper(s, "%X"); @@ -77,6 +100,9 @@ tuple read_time(const char* const s) { void test_640278(); void test_990695(); +void test_locale_russian(); +void test_locale_german(); +void test_locale_chinese(); int main() { assert(read_hour("12 AM") == 0); @@ -84,6 +110,8 @@ int main() { assert(read_hour("1 AM") == 1); assert(read_hour("1 am") == 1); + assert(read_hour("1 aM") == 1); + assert(read_hour("1 Am") == 1); assert(read_hour("2 AM") == 2); assert(read_hour("2 am") == 2); @@ -96,6 +124,8 @@ int main() { assert(read_hour("1 PM") == 13); assert(read_hour("1 pm") == 13); + assert(read_hour("1 Pm") == 13); + assert(read_hour("1 pM") == 13); assert(read_hour("2 PM") == 14); assert(read_hour("2 pm") == 14); @@ -113,6 +143,9 @@ int main() { test_640278(); test_990695(); + test_locale_russian(); + test_locale_german(); + test_locale_chinese(); } typedef istreambuf_iterator Iter; @@ -208,5 +241,289 @@ void test_990695() { assert(t.tm_mday == 31); assert(t.tm_year == 114); } + + { + istringstream iss("sep 31 2014"); + ios_base::iostate err = Bit; + tm t{}; + const string fmt("%b %d %Y"); + use_facet>(iss.getloc()) + .get(Iter(iss.rdbuf()), Iter(), iss, err, &t, fmt.c_str(), fmt.c_str() + fmt.size()); + assert(t.tm_mon == 8); + assert(t.tm_mday == 31); + assert(t.tm_year == 114); + } + + { + istringstream iss("aUG 14 2020"); + ios_base::iostate err = Bit; + tm t{}; + const string fmt("%b %d %Y"); + use_facet>(iss.getloc()) + .get(Iter(iss.rdbuf()), Iter(), iss, err, &t, fmt.c_str(), fmt.c_str() + fmt.size()); + assert(t.tm_mon == 7); + assert(t.tm_mday == 14); + assert(t.tm_year == 120); + } + + { + istringstream iss("feBRuArY 02 1991"); + ios_base::iostate err = Bit; + tm t{}; + const string fmt("%b %d %Y"); + use_facet>(iss.getloc()) + .get(Iter(iss.rdbuf()), Iter(), iss, err, &t, fmt.c_str(), fmt.c_str() + fmt.size()); + assert(t.tm_mon == 1); + assert(t.tm_mday == 2); + assert(t.tm_year == 91); + } + + { + istringstream iss("19 SKIP_THIS sEpTemBER SKIP_THIS 2005"); + ios_base::iostate err = Bit; + tm t{}; + const string fmt("%d SKIP_THIS %b SKIP_THIS %Y"); + use_facet>(iss.getloc()) + .get(Iter(iss.rdbuf()), Iter(), iss, err, &t, fmt.c_str(), fmt.c_str() + fmt.size()); + assert(t.tm_mon == 8); + assert(t.tm_mday == 19); + assert(t.tm_year == 105); + } + + { + // This case should fail + istringstream iss("2011-D-18"); + ios_base::iostate err = Bit; + tm t{}; + const string fmt("%Y-%b-%d"); + use_facet>(iss.getloc()) + .get(Iter(iss.rdbuf()), Iter(), iss, err, &t, fmt.c_str(), fmt.c_str() + fmt.size()); + assert(err == ios_base::failbit); + } + + { + // This case should fail + istringstream iss("2018-M-18"); + tm t = {}; + const string fmt("%Y-%b-%d"); + iss >> get_time(&t, fmt.c_str()); + assert(iss.fail()); + } } } + +void test_locale_russian() { + // Russian January in different cases (expanded, abbreviated, mixed cases) + assert(read_date_locale(L"2020-\x042f\x043d\x0432\x0430\x0440\x044c-05", "ru_RU.UTF-8") == make_tuple(5, 0, 120)); + assert(read_date_locale(L"2020-\x044f\x043d\x0432-05", "ru_RU.UTF-8") == make_tuple(5, 0, 120)); + assert(read_date_locale(L"2020-\x044f\x043d\x0412\x0410\x0440\x042c-05", "ru_RU.UTF-8") == make_tuple(5, 0, 120)); + assert(read_date_locale(L"2020-\x042f\x041d\x0412-05", "ru_RU.UTF-8") == make_tuple(5, 0, 120)); + + // Russian February in different cases (expanded, abbreviated, mixed cases) + assert(read_date_locale(L"2020-\x0424\x0435\x0432\x0440\x0430\x043b\x044c-15", "ru_RU.UTF-8") + == make_tuple(15, 1, 120)); + assert(read_date_locale(L"2020-\x0444\x0435\x0432-15", "ru_RU.UTF-8") == make_tuple(15, 1, 120)); + assert(read_date_locale(L"2020-\x0444\x0435\x0412\x0440\x0410\x043b\x044c-15", "ru_RU.UTF-8") + == make_tuple(15, 1, 120)); + assert(read_date_locale(L"2020-\x0424\x0435\x0412-15", "ru_RU.UTF-8") == make_tuple(15, 1, 120)); + + // Russian March in different cases (expanded, abbreviated, mixed cases) + assert(read_date_locale(L"2020-\x041c\x0430\x0440\x0442-25", "ru_RU.UTF-8") == make_tuple(25, 2, 120)); + assert(read_date_locale(L"2020-\x043c\x0430\x0440-25", "ru_RU.UTF-8") == make_tuple(25, 2, 120)); + assert(read_date_locale(L"2020-\x041c\x0430\x0420\x0442-25", "ru_RU.UTF-8") == make_tuple(25, 2, 120)); + assert(read_date_locale(L"2020-\x041c\x0430\x0420-25", "ru_RU.UTF-8") == make_tuple(25, 2, 120)); + + // Russian April in different cases (expanded, abbreviated, mixed cases) + assert(read_date_locale(L"2020-\x0410\x043f\x0440\x0435\x043b\x044c-05", "ru_RU.UTF-8") == make_tuple(5, 3, 120)); + assert(read_date_locale(L"2020-\x0430\x043f\x0440-05", "ru_RU.UTF-8") == make_tuple(5, 3, 120)); + assert(read_date_locale(L"2020-\x0410\x043f\x0420\x0415\x043b\x044c-05", "ru_RU.UTF-8") == make_tuple(5, 3, 120)); + assert(read_date_locale(L"2020-\x0430\x041f\x0420-05", "ru_RU.UTF-8") == make_tuple(5, 3, 120)); + + // Russian May in different cases (expanded, mixed cases) + // Expanded and abbreviated versions are identical + assert(read_date_locale(L"2020-\x041c\x0430\x0439-15", "ru_RU.UTF-8") == make_tuple(15, 4, 120)); + assert(read_date_locale(L"2020-\x043c\x0410\x0419-15", "ru_RU.UTF-8") == make_tuple(15, 4, 120)); + + // Russian June in different cases (expanded, abbreviated, mixed cases) + assert(read_date_locale(L"2020-\x0418\x044e\x043d\x044c-25", "ru_RU.UTF-8") == make_tuple(25, 5, 120)); + assert(read_date_locale(L"2020-\x0438\x044e\x043d-25", "ru_RU.UTF-8") == make_tuple(25, 5, 120)); + assert(read_date_locale(L"2020-\x0418\x044e\x041d\x042c-25", "ru_RU.UTF-8") == make_tuple(25, 5, 120)); + assert(read_date_locale(L"2020-\x0438\x042e\x041d-25", "ru_RU.UTF-8") == make_tuple(25, 5, 120)); + + // Russian July in different cases (expanded, abbreviated, mixed cases) + assert(read_date_locale(L"2020-\x0418\x044e\x043b\x044c-12", "ru_RU.UTF-8") == make_tuple(12, 6, 120)); + assert(read_date_locale(L"2020-\x0438\x044e\x043b-12", "ru_RU.UTF-8") == make_tuple(12, 6, 120)); + assert(read_date_locale(L"2020-\x0418\x044e\x041b\x044c-12", "ru_RU.UTF-8") == make_tuple(12, 6, 120)); + assert(read_date_locale(L"2020-\x0418\x044e\x041b-12", "ru_RU.UTF-8") == make_tuple(12, 6, 120)); + + // Russian August in different cases (expanded, abbreviated, mixed cases) + assert(read_date_locale(L"2020-\x0410\x0432\x0433\x0443\x0441\x0442-02", "ru_RU.UTF-8") == make_tuple(2, 7, 120)); + assert(read_date_locale(L"2020-\x0430\x0432\x0433-02", "ru_RU.UTF-8") == make_tuple(2, 7, 120)); + assert(read_date_locale(L"2020-\x0410\x0432\x0433\x0423\x0421\x0442-02", "ru_RU.UTF-8") == make_tuple(2, 7, 120)); + assert(read_date_locale(L"2020-\x0430\x0412\x0413-02", "ru_RU.UTF-8") == make_tuple(2, 7, 120)); + + // Russian September in different cases (expanded, abbreviated, mixed cases) + assert(read_date_locale(L"2020-\x0421\x0435\x043d\x0442\x044f\x0431\x0440\x044c-21", "ru_RU.UTF-8") + == make_tuple(21, 8, 120)); + assert(read_date_locale(L"2020-\x0441\x0435\x043d-21", "ru_RU.UTF-8") == make_tuple(21, 8, 120)); + assert(read_date_locale(L"2020-\x0421\x0435\x043d\x0442\x044f\x0411\x0440\x044c-21", "ru_RU.UTF-8") + == make_tuple(21, 8, 120)); + assert(read_date_locale(L"2020-\x0441\x0415\x041d-21", "ru_RU.UTF-8") == make_tuple(21, 8, 120)); + + // Russian October in different cases (expanded, abbreviated, mixed cases) + assert(read_date_locale(L"2020-\x041e\x043a\x0442\x044f\x0431\x0440\x044c-01", "ru_RU.UTF-8") + == make_tuple(1, 9, 120)); + assert(read_date_locale(L"2020-\x043e\x043a\x0442-01", "ru_RU.UTF-8") == make_tuple(1, 9, 120)); + assert(read_date_locale(L"2020-\x041e\x043a\x0442\x044f\x0411\x0440\x044c-01", "ru_RU.UTF-8") + == make_tuple(1, 9, 120)); + assert(read_date_locale(L"2020-\x043e\x041a\x0442-01", "ru_RU.UTF-8") == make_tuple(1, 9, 120)); + + // Russian November in different cases (expanded, abbreviated, mixed cases) + assert(read_date_locale(L"2020-\x041d\x043e\x044f\x0431\x0440\x044c-09", "ru_RU.UTF-8") == make_tuple(9, 10, 120)); + assert(read_date_locale(L"2020-\x043d\x043e\x044f-09", "ru_RU.UTF-8") == make_tuple(9, 10, 120)); + assert(read_date_locale(L"2020-\x041d\x043e\x044f\x0411\x0440\x044c-09", "ru_RU.UTF-8") == make_tuple(9, 10, 120)); + assert(read_date_locale(L"2020-\x043d\x041e\x042f-09", "ru_RU.UTF-8") == make_tuple(9, 10, 120)); + + // Russian December in different cases (expanded, abbreviated, mixed cases) + assert(read_date_locale(L"2020-\x0414\x0435\x043a\x0430\x0431\x0440\x044c-31", "ru_RU.UTF-8") + == make_tuple(31, 11, 120)); + assert(read_date_locale(L"2020-\x0434\x0435\x043a-31", "ru_RU.UTF-8") == make_tuple(31, 11, 120)); + assert(read_date_locale(L"2020-\x0414\x0435\x043a\x0430\x0411\x0440\x044c-31", "ru_RU.UTF-8") + == make_tuple(31, 11, 120)); + assert(read_date_locale(L"2020-\x0434\x0415\x043a-31", "ru_RU.UTF-8") == make_tuple(31, 11, 120)); +} + +void test_locale_german() { + // German January in different cases (expanded, abbreviated, mixed cases) + assert(read_date_locale(L"2020-\x004a\x0061\x006e\x0075\x0061\x0072-05", "de_DE.utf-8") == make_tuple(5, 0, 120)); + assert(read_date_locale(L"2020-\x004a\x0061\x006e-05", "de_DE.utf-8") == make_tuple(5, 0, 120)); + assert(read_date_locale(L"2020-\x004a\x0061\x006e\x0055\x0041\x0072-05", "de_DE.utf-8") == make_tuple(5, 0, 120)); + assert(read_date_locale(L"2020-\x006a\x0041\x004e-05", "de_DE.utf-8") == make_tuple(5, 0, 120)); + + // German February in different cases (expanded, abbreviated, mixed cases) + assert(read_date_locale(L"2020-\x0046\x0065\x0062\x0072\x0075\x0061\x0072-15", "de_DE.utf-8") + == make_tuple(15, 1, 120)); + assert(read_date_locale(L"2020-\x0046\x0065\x0062-15", "de_DE.utf-8") == make_tuple(15, 1, 120)); + assert(read_date_locale(L"2020-\x0046\x0065\x0062\x0072\x0055\x0061\x0072-15", "de_DE.utf-8") + == make_tuple(15, 1, 120)); + assert(read_date_locale(L"2020-\x0066\x0045\x0062-15", "de_DE.utf-8") == make_tuple(15, 1, 120)); + + // German March in different cases (expanded, abbreviated, mixed cases) + assert(read_date_locale(L"2020-\x004d\x00e4\x0072\x007a-25", "de_DE.utf-8") == make_tuple(25, 2, 120)); + assert(read_date_locale(L"2020-\x004d\x0072\x007a-25", "de_DE.utf-8") == make_tuple(25, 2, 120)); + assert(read_date_locale(L"2020-\x006d\x00e4\x0052\x005a-25", "de_DE.utf-8") == make_tuple(25, 2, 120)); + assert(read_date_locale(L"2020-\x006d\x0052\x005a-25", "de_DE.utf-8") == make_tuple(25, 2, 120)); + + // German April in different cases (expanded, abbreviated, mixed cases) + assert(read_date_locale(L"2020-\x0041\x0070\x0072\x0069\x006c-05", "de_DE.utf-8") == make_tuple(5, 3, 120)); + assert(read_date_locale(L"2020-\x0041\x0070\x0072-05", "de_DE.utf-8") == make_tuple(5, 3, 120)); + assert(read_date_locale(L"2020-\x0061\x0070\x0052\x0069\x004c-05", "de_DE.utf-8") == make_tuple(5, 3, 120)); + assert(read_date_locale(L"2020-\x0061\x0050\x0052-05", "de_DE.utf-8") == make_tuple(5, 3, 120)); + + // German May in different cases (expanded, mixed cases) + // Expanded and abbreviated versions are identical + assert(read_date_locale(L"2020-\x004d\x0061\x0069-15", "de_DE.utf-8") == make_tuple(15, 4, 120)); + assert(read_date_locale(L"2020-\x006d\x0041\x0069-15", "de_DE.utf-8") == make_tuple(15, 4, 120)); + + // German June in different cases (expanded, abbreviated, mixed cases) + assert(read_date_locale(L"2020-\x004a\x0075\x006e\x0069-25", "de_DE.utf-8") == make_tuple(25, 5, 120)); + assert(read_date_locale(L"2020-\x004a\x0075\x006e-25", "de_DE.utf-8") == make_tuple(25, 5, 120)); + assert(read_date_locale(L"2020-\x006a\x0055\x004e\x0069-25", "de_DE.utf-8") == make_tuple(25, 5, 120)); + assert(read_date_locale(L"2020-\x006a\x0055\x004e-25", "de_DE.utf-8") == make_tuple(25, 5, 120)); + + // German July in different cases (expanded, mixed cases) + // Expanded and abbreviated are identical + assert(read_date_locale(L"2020-\x004a\x0075\x006c\x0069-12", "de_DE.utf-8") == make_tuple(12, 6, 120)); + assert(read_date_locale(L"2020-\x004a\x0075\x004c\x0069-12", "de_DE.utf-8") == make_tuple(12, 6, 120)); + + // German August in different cases (expanded, abbreviated, mixed cases) + assert(read_date_locale(L"2020-\x0041\x0075\x0067\x0075\x0073\x0074-02", "de_DE.utf-8") == make_tuple(2, 7, 120)); + assert(read_date_locale(L"2020-\x0041\x0075\x0067-02", "de_DE.utf-8") == make_tuple(2, 7, 120)); + assert(read_date_locale(L"2020-\x0061\x0075\x0047\x0075\x0053\x0074-02", "de_DE.utf-8") == make_tuple(2, 7, 120)); + assert(read_date_locale(L"2020-\x0061\x0055\x0047-02", "de_DE.utf-8") == make_tuple(2, 7, 120)); + + // German September in different cases (expanded, abbreviated, mixed cases) + assert(read_date_locale(L"2020-\x0053\x0065\x0070\x0074\x0065\x006d\x0062\x0065\x0072-21", "de_DE.utf-8") + == make_tuple(21, 8, 120)); + assert(read_date_locale(L"2020-\x0053\x0065\x0070-21", "de_DE.utf-8") == make_tuple(21, 8, 120)); + assert(read_date_locale(L"2020-\x0073\x0045\x0070\x0054\x0065\x004d\x0062\x0065\x0072-21", "de_DE.utf-8") + == make_tuple(21, 8, 120)); + assert(read_date_locale(L"2020-\x0053\x0065\x0070-21", "de_DE.utf-8") == make_tuple(21, 8, 120)); + assert(read_date_locale(L"2020-\x0073\x0045\x0050-21", "de_DE.utf-8") == make_tuple(21, 8, 120)); + + // German October in different cases (expanded, abbreviated, mixed cases) + assert(read_date_locale(L"2020-\x004f\x006b\x0074\x006f\x0062\x0065\x0072-01", "de_DE.utf-8") + == make_tuple(1, 9, 120)); + assert(read_date_locale(L"2020-\x004f\x006b\x0074-01", "de_DE.utf-8") == make_tuple(1, 9, 120)); + assert(read_date_locale(L"2020-\x006f\x004b\x0074\x006f\x0042\x0065\x0052-01", "de_DE.utf-8") + == make_tuple(1, 9, 120)); + assert(read_date_locale(L"2020-\x006f\x004b\x0074-01", "de_DE.utf-8") == make_tuple(1, 9, 120)); + + // German November in different cases (expanded, abbreviated, mixed cases) + assert(read_date_locale(L"2020-\x004e\x006f\x0076\x0065\x006d\x0062\x0065\x0072-09", "de_DE.utf-8") + == make_tuple(9, 10, 120)); + assert(read_date_locale(L"2020-\x004e\x006f\x0076-09", "de_DE.utf-8") == make_tuple(9, 10, 120)); + assert(read_date_locale(L"2020-\x006e\x006f\x0056\x0065\x006d\x0042\x0065\x0052-09", "de_DE.utf-8") + == make_tuple(9, 10, 120)); + assert(read_date_locale(L"2020-\x006e\x004f\x0056-09", "de_DE.utf-8") == make_tuple(9, 10, 120)); + + // German December in different cases (expanded, abbreviated, mixed cases) + assert(read_date_locale(L"2020-\x0044\x0065\x007a\x0065\x006d\x0062\x0065\x0072-31", "de_DE.utf-8") + == make_tuple(31, 11, 120)); + assert(read_date_locale(L"2020-\x0044\x0065\x007a-31", "de_DE.utf-8") == make_tuple(31, 11, 120)); + assert(read_date_locale(L"2020-\x0064\x0065\x005a\x0065\x004d\x0062\x0045\x0072-31", "de_DE.utf-8") + == make_tuple(31, 11, 120)); + assert(read_date_locale(L"2020-\x0064\x0045\x005a-31", "de_DE.utf-8") == make_tuple(31, 11, 120)); +} + +void test_locale_chinese() { + // Chinese letters don't have distinct upper and lower cases + + // January in Chinese (expanded and abbreviated) + assert(read_date_locale(L"2020-\x4e00\x6708-05", "zh-CN.utf-8") == make_tuple(5, 0, 120)); + assert(read_date_locale(L"2020-\x0031\x6708-05", "zh-CN.utf-8") == make_tuple(5, 0, 120)); + + // February in Chinese (expanded and abbreviated) + assert(read_date_locale(L"2020-\x4e8c\x6708-15", "zh-CN.utf-8") == make_tuple(15, 1, 120)); + assert(read_date_locale(L"2020-\x0032\x6708-15", "zh-CN.utf-8") == make_tuple(15, 1, 120)); + + // March in Chinese (expanded and abbreviated) + assert(read_date_locale(L"2020-\x4e09\x6708-25", "zh-CN.utf-8") == make_tuple(25, 2, 120)); + assert(read_date_locale(L"2020-\x0033\x6708-25", "zh-CN.utf-8") == make_tuple(25, 2, 120)); + + // April in Chinese (expanded and abbreviated) + assert(read_date_locale(L"2020-\x56db\x6708-05", "zh-CN.utf-8") == make_tuple(5, 3, 120)); + assert(read_date_locale(L"2020-\x0034\x6708-05", "zh-CN.utf-8") == make_tuple(5, 3, 120)); + + // May in Chinese (expanded and abbreviated) + assert(read_date_locale(L"2020-\x4e94\x6708-15", "zh-CN.utf-8") == make_tuple(15, 4, 120)); + assert(read_date_locale(L"2020-\x0035\x6708-15", "zh-CN.utf-8") == make_tuple(15, 4, 120)); + + // June in Chinese (expanded and abbreviated) + assert(read_date_locale(L"2020-\x516d\x6708-25", "zh-CN.utf-8") == make_tuple(25, 5, 120)); + assert(read_date_locale(L"2020-\x0036\x6708-25", "zh-CN.utf-8") == make_tuple(25, 5, 120)); + + // July in Chinese (expanded and abbreviated) + assert(read_date_locale(L"2020-\x4e03\x6708-12", "zh-CN.utf-8") == make_tuple(12, 6, 120)); + assert(read_date_locale(L"2020-\x0037\x6708-12", "zh-CN.utf-8") == make_tuple(12, 6, 120)); + + // August in Chinese (expanded and abbreviated) + assert(read_date_locale(L"2020-\x516b\x6708-02", "zh-CN.utf-8") == make_tuple(2, 7, 120)); + assert(read_date_locale(L"2020-\x0038\x6708-02", "zh-CN.utf-8") == make_tuple(2, 7, 120)); + + // September in Chinese (expanded and abbreviated) + assert(read_date_locale(L"2020-\x4e5d\x6708-21", "zh-CN.utf-8") == make_tuple(21, 8, 120)); + assert(read_date_locale(L"2020-\x0039\x6708-21", "zh-CN.utf-8") == make_tuple(21, 8, 120)); + + // October in Chinese (expanded and abbreviated) + assert(read_date_locale(L"2020-\x5341\x6708-01", "zh-CN.utf-8") == make_tuple(1, 9, 120)); + assert(read_date_locale(L"2020-\x0031\x0030\x6708-01", "zh-CN.utf-8") == make_tuple(1, 9, 120)); + + // November in Chinese (expanded and abbreviated) + assert(read_date_locale(L"2020-\x5341\x4e00\x6708-09", "zh-CN.utf-8") == make_tuple(9, 10, 120)); + assert(read_date_locale(L"2020-\x0031\x0031\x6708-09", "zh-CN.utf-8") == make_tuple(9, 10, 120)); + + // December in Chinese (expanded and abbreviated) + assert(read_date_locale(L"2020-\x5341\x4e8c\x6708-31", "zh-CN.utf-8") == make_tuple(31, 11, 120)); + assert(read_date_locale(L"2020-\x0031\x0032\x6708-31", "zh-CN.utf-8") == make_tuple(31, 11, 120)); +}