get_time() should ignore case when parsing days and months (#1168)

Fixes #1126.

Co-authored-by: Stephan T. Lavavej <stl@microsoft.com>
This commit is contained in:
Hamid Reza Arzaghi 2020-08-26 11:43:30 +04:30 коммит произвёл GitHub
Родитель fb11d09bc9
Коммит ea1aaf76cb
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
3 изменённых файлов: 366 добавлений и 43 удалений

Просмотреть файл

@ -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 <class _Elem, class _InIt>
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<char>(_Column < 127 ? _Column : 127); // save skip count if small enough
_Ans = static_cast<int>(_Field); // save answer
} else if (_First == _Last || _Ptr[_Off] != *_First) {
_Str[_Field] = static_cast<char>(_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 <class _Elem>
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 <class _Elem, class _InIt>
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<ctype<_Elem>>(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<char>(_Column < 127 ? _Column : 127); // save skip count if small enough
_Ans = static_cast<int>(_Field); // save answer
} else if (_First == _Last || _CType.tolower(_Ptr[_Off]) != _CType.tolower(static_cast<_Elem>(*_First))) {
_Str[_Field] = static_cast<char>(_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<char, char, mbstate_t>;

Просмотреть файл

@ -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;
}
}
}

Просмотреть файл

@ -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<bool>(wss));
return t;
}
int read_hour(const char* const s) {
const auto t = helper(s, "%I %p");
@ -59,6 +71,17 @@ tuple<int, int, int> read_date(const char* const s) {
return make_tuple(t.tm_mday, t.tm_mon, t.tm_year);
}
tuple<int, int, int> 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<int, int, int> read_time(const char* const s) {
const auto t = helper(s, "%X");
@ -77,6 +100,9 @@ tuple<int, int, int> 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<char> 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<time_get<char>>(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<time_get<char>>(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<time_get<char>>(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<time_get<char>>(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<time_get<char>>(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));
}