From 6cd0eb202726616a1c3d9e59915be90016e42862 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=C3=A1n=20de=20B=C3=BArca?= Date: Wed, 24 Dec 2014 11:52:04 -0500 Subject: [PATCH] kernel32: Implement Get{Time,Date}FormatEx. --- .../api-ms-win-core-datetime-l1-1-1.spec | 4 +- dlls/kernel32/kernel32.spec | 2 + dlls/kernel32/lcformat.c | 71 +++++ dlls/kernel32/tests/locale.c | 300 ++++++++++++++++++ include/winnls.h | 2 + 5 files changed, 377 insertions(+), 2 deletions(-) diff --git a/dlls/api-ms-win-core-datetime-l1-1-1/api-ms-win-core-datetime-l1-1-1.spec b/dlls/api-ms-win-core-datetime-l1-1-1/api-ms-win-core-datetime-l1-1-1.spec index 56309299535..e3aba5a7120 100644 --- a/dlls/api-ms-win-core-datetime-l1-1-1/api-ms-win-core-datetime-l1-1-1.spec +++ b/dlls/api-ms-win-core-datetime-l1-1-1/api-ms-win-core-datetime-l1-1-1.spec @@ -1,6 +1,6 @@ @ stdcall GetDateFormatA(long long ptr str ptr long) kernel32.GetDateFormatA -@ stub GetDateFormatEx +@ stdcall GetDateFormatEx(wstr long ptr wstr ptr long wstr) kernel32.GetDateFormatEx @ stdcall GetDateFormatW(long long ptr wstr ptr long) kernel32.GetDateFormatW @ stdcall GetTimeFormatA(long long ptr str ptr long) kernel32.GetTimeFormatA -@ stub GetTimeFormatEx +@ stdcall GetTimeFormatEx(wstr long ptr wstr ptr long) kernel32.GetTimeFormatEx @ stdcall GetTimeFormatW(long long ptr wstr ptr long) kernel32.GetTimeFormatW diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec index c79b5d70c4d..bd6d644bc48 100644 --- a/dlls/kernel32/kernel32.spec +++ b/dlls/kernel32/kernel32.spec @@ -510,6 +510,7 @@ @ stdcall -norelay GetCurrentThread() @ stdcall -norelay GetCurrentThreadId() @ stdcall GetDateFormatA(long long ptr str ptr long) +@ stdcall GetDateFormatEx(wstr long ptr wstr ptr long wstr) @ stdcall GetDateFormatW(long long ptr wstr ptr long) @ stdcall GetDaylightFlag() @ stdcall GetDefaultCommConfigA(str ptr long) @@ -680,6 +681,7 @@ @ stdcall GetTickCount() @ stdcall -ret64 GetTickCount64() @ stdcall GetTimeFormatA(long long ptr str ptr long) +@ stdcall GetTimeFormatEx(wstr long ptr wstr ptr long) @ stdcall GetTimeFormatW(long long ptr wstr ptr long) @ stdcall GetTimeZoneInformation(ptr) @ stdcall GetThreadUILanguage() diff --git a/dlls/kernel32/lcformat.c b/dlls/kernel32/lcformat.c index c1af2533a42..8ec1d8a311b 100644 --- a/dlls/kernel32/lcformat.c +++ b/dlls/kernel32/lcformat.c @@ -846,6 +846,45 @@ INT WINAPI GetDateFormatA( LCID lcid, DWORD dwFlags, const SYSTEMTIME* lpTime, lpFormat, lpDateStr, cchOut); } +/****************************************************************************** + * GetDateFormatEx [KERNEL32.@] + * + * Format a date for a given locale. + * + * PARAMS + * localename [I] Locale to format for + * flags [I] LOCALE_ and DATE_ flags from "winnls.h" + * date [I] Date to format + * format [I] Format string, or NULL to use the locale defaults + * outbuf [O] Destination for formatted string + * bufsize [I] Size of outbuf, or 0 to calculate the resulting size + * calendar [I] Reserved, must be NULL + * + * See GetDateFormatA for notes. + * + * RETURNS + * Success: The number of characters written to outbuf, or that would have + * been written if bufsize is 0. + * Failure: 0. Use GetLastError() to determine the cause. + */ +INT WINAPI GetDateFormatEx(LPCWSTR localename, DWORD flags, + const SYSTEMTIME* date, LPCWSTR format, + LPWSTR outbuf, INT bufsize, LPCWSTR calendar) +{ + TRACE("(%s,0x%08x,%p,%s,%p,%d,%s)\n", debugstr_w(localename), flags, + date, debugstr_w(format), outbuf, bufsize, debugstr_w(calendar)); + + /* Parameter is currently reserved and Windows errors if set */ + if (calendar != NULL) + { + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + + return NLS_GetDateTimeFormatW(LocaleNameToLCID(localename, 0), + flags | DATE_DATEVARSONLY, date, format, + outbuf, bufsize); +} /****************************************************************************** * GetDateFormatW [KERNEL32.@] @@ -913,6 +952,38 @@ INT WINAPI GetTimeFormatA(LCID lcid, DWORD dwFlags, const SYSTEMTIME* lpTime, lpFormat, lpTimeStr, cchOut); } +/****************************************************************************** + * GetTimeFormatEx [KERNEL32.@] + * + * Format a date for a given locale. + * + * PARAMS + * localename [I] Locale to format for + * flags [I] LOCALE_ and TIME_ flags from "winnls.h" + * time [I] Time to format + * format [I] Formatting overrides + * outbuf [O] Destination for formatted string + * bufsize [I] Size of outbuf, or 0 to calculate the resulting size + * + * See GetTimeFormatA for notes. + * + * RETURNS + * Success: The number of characters written to outbuf, or that would have + * have been written if bufsize is 0. + * Failure: 0. Use GetLastError() to determine the cause. + */ +INT WINAPI GetTimeFormatEx(LPCWSTR localename, DWORD flags, + const SYSTEMTIME* time, LPCWSTR format, + LPWSTR outbuf, INT bufsize) +{ + TRACE("(%s,0x%08x,%p,%s,%p,%d)\n", debugstr_w(localename), flags, time, + debugstr_w(format), outbuf, bufsize); + + return NLS_GetDateTimeFormatW(LocaleNameToLCID(localename, 0), + flags | TIME_TIMEVARSONLY, time, format, + outbuf, bufsize); +} + /****************************************************************************** * GetTimeFormatW [KERNEL32.@] * diff --git a/dlls/kernel32/tests/locale.c b/dlls/kernel32/tests/locale.c index 65172a7f17d..95e0ac69132 100644 --- a/dlls/kernel32/tests/locale.c +++ b/dlls/kernel32/tests/locale.c @@ -39,7 +39,9 @@ static const WCHAR upper_case[] = {'\t','J','U','S','T','!',' ','A',',',' ','T','E','S','T',';',' ','S','T','R','I','N','G',' ','1','/','*','+','-','.','\r','\n',0}; static const WCHAR lower_case[] = {'\t','j','u','s','t','!',' ','a',',',' ','t','e','s','t',';',' ','s','t','r','i','n','g',' ','1','/','*','+','-','.','\r','\n',0}; static const WCHAR symbols_stripped[] = {'j','u','s','t','a','t','e','s','t','s','t','r','i','n','g','1',0}; +static const WCHAR localeW[] = {'e','n','-','U','S',0}; static const WCHAR fooW[] = {'f','o','o',0}; +static const WCHAR emptyW[] = {0}; static inline unsigned int strlenW( const WCHAR *str ) { @@ -72,6 +74,8 @@ static inline BOOL isdigitW( WCHAR wc ) static HMODULE hKernel32; static WORD enumCount; +static INT (WINAPI *pGetTimeFormatEx)(LPCWSTR, DWORD, const SYSTEMTIME *, LPCWSTR, LPWSTR, INT); +static INT (WINAPI *pGetDateFormatEx)(LPCWSTR, DWORD, const SYSTEMTIME *, LPCWSTR, LPWSTR, INT, LPCWSTR); static BOOL (WINAPI *pEnumSystemLanguageGroupsA)(LANGUAGEGROUP_ENUMPROCA, DWORD, LONG_PTR); static BOOL (WINAPI *pEnumLanguageGroupLocalesA)(LANGGROUPLOCALE_ENUMPROCA, LGRPID, DWORD, LONG_PTR); static BOOL (WINAPI *pEnumUILanguagesA)(UILANGUAGE_ENUMPROCA, DWORD, LONG_PTR); @@ -99,6 +103,8 @@ static void InitFunctionPointers(void) hKernel32 = GetModuleHandleA("kernel32"); #define X(f) p##f = (void*)GetProcAddress(hKernel32, #f) + X(GetTimeFormatEx); + X(GetDateFormatEx); X(EnumSystemLanguageGroupsA); X(EnumLanguageGroupLocalesA); X(LocaleNameToLCID); @@ -612,6 +618,202 @@ static void test_GetTimeFormatA(void) EXPECT_LENA; EXPECT_EQA; } +static void test_GetTimeFormatEx(void) +{ + int ret; + SYSTEMTIME curtime; + WCHAR buffer[BUFFER_SIZE], input[BUFFER_SIZE], Expected[BUFFER_SIZE]; + + if (!pGetTimeFormatEx) + { + win_skip("GetTimeFormatEx not supported\n"); + return; + } + + memset(&curtime, 2, sizeof(SYSTEMTIME)); + STRINGSW("tt HH':'mm'@'ss", ""); /* Invalid time */ + SetLastError(0xdeadbeef); + ret = pGetTimeFormatEx(localeW, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, COUNTOF(buffer)); + ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); + + curtime.wHour = 8; + curtime.wMinute = 56; + curtime.wSecond = 13; + curtime.wMilliseconds = 22; + STRINGSW("tt HH':'mm'@'ss", "AM 08:56@13"); /* Valid time */ + SetLastError(0xdeadbeef); + ret = pGetTimeFormatEx(localeW, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, COUNTOF(buffer)); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + /* MSDN: LOCALE_NOUSEROVERRIDE can't be specified with a format string */ + SetLastError(0xdeadbeef); + ret = pGetTimeFormatEx(localeW, NUO|TIME_FORCE24HOURFORMAT, &curtime, input, buffer, COUNTOF(buffer)); + ok(!ret && GetLastError() == ERROR_INVALID_FLAGS, + "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError()); + + STRINGSW("tt HH':'mm'@'ss", "A"); /* Insufficient buffer */ + SetLastError(0xdeadbeef); + ret = pGetTimeFormatEx(localeW, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, 2); + ok( !ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, + "Expected ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError()); + + STRINGSW("tt HH':'mm'@'ss", "AM 08:56@13"); /* Calculate length only */ + ret = pGetTimeFormatEx(localeW, TIME_FORCE24HOURFORMAT, &curtime, input, NULL, 0); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; + + STRINGSW("", "8 AM"); /* TIME_NOMINUTESORSECONDS, default format */ + ret = pGetTimeFormatEx(localeW, NUO|TIME_NOMINUTESORSECONDS, &curtime, NULL, buffer, COUNTOF(buffer)); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + STRINGSW("m1s2m3s4", ""); /* TIME_NOMINUTESORSECONDS/complex format */ + ret = pGetTimeFormatEx(localeW, TIME_NOMINUTESORSECONDS, &curtime, input, buffer, COUNTOF(buffer)); + ok(ret == strlenW(buffer)+1, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + STRINGSW("", "8:56 AM"); /* TIME_NOSECONDS/Default format */ + ret = pGetTimeFormatEx(localeW, NUO|TIME_NOSECONDS, &curtime, NULL, buffer, COUNTOF(buffer)); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + STRINGSW("h:m:s tt", "8:56 AM"); /* TIME_NOSECONDS */ + ret = pGetTimeFormatEx(localeW, TIME_NOSECONDS, &curtime, input, buffer, COUNTOF(buffer)); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + STRINGSW("h.@:m.@:s.@:tt", "8.@:56AM"); /* Multiple delimiters */ + ret = pGetTimeFormatEx(localeW, TIME_NOSECONDS, &curtime, input, buffer, COUNTOF(buffer)); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + STRINGSW("s1s2s3", ""); /* Duplicate tokens */ + ret = pGetTimeFormatEx(localeW, TIME_NOSECONDS, &curtime, input, buffer, COUNTOF(buffer)); + ok(ret == strlenW(buffer)+1, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + STRINGSW("t/tt", "A/AM"); /* AM time marker */ + ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, COUNTOF(buffer)); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + curtime.wHour = 13; + STRINGSW("t/tt", "P/PM"); /* PM time marker */ + ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, COUNTOF(buffer)); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + STRINGSW("h1t2tt3m", "156"); /* TIME_NOTIMEMARKER: removes text around time marker token */ + ret = pGetTimeFormatEx(localeW, TIME_NOTIMEMARKER, &curtime, input, buffer, COUNTOF(buffer)); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + STRINGSW("h:m:s tt", "13:56:13 PM"); /* TIME_FORCE24HOURFORMAT */ + ret = pGetTimeFormatEx(localeW, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, COUNTOF(buffer)); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + STRINGSW("h:m:s", "13:56:13"); /* TIME_FORCE24HOURFORMAT doesn't add time marker */ + ret = pGetTimeFormatEx(localeW, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, COUNTOF(buffer)); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + curtime.wHour = 14; /* change this to 14 or 2pm */ + curtime.wMinute = 5; + curtime.wSecond = 3; + STRINGSW("h hh H HH m mm s ss t tt", "2 02 14 14 5 05 3 03 P PM"); /* 24 hrs, leading 0 */ + ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, COUNTOF(buffer)); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + curtime.wHour = 0; + STRINGSW("h/H/hh/HH", "12/0/12/00"); /* "hh" and "HH" */ + ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, COUNTOF(buffer)); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + STRINGSW("h:m:s tt", "12:5:3 AM"); /* non-zero flags should fail with format, doesn't */ + ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, COUNTOF(buffer)); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + /* try to convert formatting strings with more than two letters + * "h:hh:hhh:H:HH:HHH:m:mm:mmm:M:MM:MMM:s:ss:sss:S:SS:SSS" + * NOTE: We expect any letter for which there is an upper case value + * we should see a replacement. For letters that DO NOT have + * upper case values we should see NO REPLACEMENT. + */ + curtime.wHour = 8; + curtime.wMinute = 56; + curtime.wSecond = 13; + curtime.wMilliseconds = 22; + STRINGSW("h:hh:hhh H:HH:HHH m:mm:mmm M:MM:MMM s:ss:sss S:SS:SSS", + "8:08:08 8:08:08 56:56:56 M:MM:MMM 13:13:13 S:SS:SSS"); + ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, COUNTOF(buffer)); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + STRINGSW("h", "text"); /* Don't write to buffer if len is 0 */ + lstrcpyW(buffer, Expected); + ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, 0); + ok(ret == 2, "Expected ret == 2, got %d, error %d\n", ret, GetLastError()); + EXPECT_EQW; + + STRINGSW("h 'h' H 'H' HH 'HH' m 'm' s 's' t 't' tt 'tt'", + "8 h 8 H 08 HH 56 m 13 s A t AM tt"); /* "'" preserves tokens */ + ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, COUNTOF(buffer)); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + STRINGSW("'''", "'"); /* invalid quoted string */ + ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, COUNTOF(buffer)); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + /* test that msdn suggested single quotation usage works as expected */ + STRINGSW("''''", "'"); /* single quote mark */ + ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, COUNTOF(buffer)); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + STRINGSW("''HHHHHH", "08"); /* Normal use */ + ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, COUNTOF(buffer)); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + /* and test for normal use of the single quotation mark */ + STRINGSW("'''HHHHHH'", "'HHHHHH"); /* Normal use */ + ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, COUNTOF(buffer)); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + STRINGSW("'''HHHHHH", "'HHHHHH"); /* Odd use */ + ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, COUNTOF(buffer)); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + STRINGSW("'123'tt", ""); /* TIME_NOTIMEMARKER drops literals too */ + ret = pGetTimeFormatEx(localeW, TIME_NOTIMEMARKER, &curtime, input, buffer, COUNTOF(buffer)); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + curtime.wHour = 25; + STRINGSW("'123'tt", ""); /* Invalid time */ + SetLastError(0xdeadbeef); + ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, COUNTOF(buffer)); + ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); + + curtime.wHour = 12; + curtime.wMonth = 60; /* Invalid */ + STRINGSW("h:m:s", "12:56:13"); /* Invalid date */ + ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, COUNTOF(buffer)); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; +} + static void test_GetDateFormatA(void) { int ret; @@ -779,6 +981,102 @@ static void test_GetDateFormatA(void) "Expected '%s', got '%s'\n", Expected, buffer); } +static void test_GetDateFormatEx(void) +{ + int ret; + SYSTEMTIME curtime; + WCHAR buffer[BUFFER_SIZE], input[BUFFER_SIZE], Expected[BUFFER_SIZE]; + + if (!pGetDateFormatEx) + { + win_skip("GetDateFormatEx not supported\n"); + return; + } + + STRINGSW("",""); /* If flags are set, then format must be NULL */ + SetLastError(0xdeadbeef); + ret = pGetDateFormatEx(localeW, DATE_LONGDATE, NULL, + input, buffer, COUNTOF(buffer), NULL); + ok(!ret && GetLastError() == ERROR_INVALID_FLAGS, + "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError()); + EXPECT_EQW; + + STRINGSW("",""); /* NULL buffer, len > 0 */ + SetLastError(0xdeadbeef); + ret = pGetDateFormatEx(localeW, 0, NULL, input, NULL, COUNTOF(buffer), NULL); + ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); + + STRINGSW("",""); /* NULL buffer, len == 0 */ + ret = pGetDateFormatEx(localeW, 0, NULL, input, NULL, 0, NULL); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + STRINGSW("",""); /* Invalid flag combination */ + SetLastError(0xdeadbeef); + ret = pGetDateFormatEx(localeW, DATE_LONGDATE|DATE_SHORTDATE, NULL, + input, NULL, 0, NULL); + ok(!ret && GetLastError() == ERROR_INVALID_FLAGS, + "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError()); + EXPECT_EQW; + + curtime.wYear = 2002; + curtime.wMonth = 10; + curtime.wDay = 23; + curtime.wDayOfWeek = 45612; /* Should be 3 - Wednesday */ + curtime.wHour = 65432; /* Invalid */ + curtime.wMinute = 34512; /* Invalid */ + curtime.wSecond = 65535; /* Invalid */ + curtime.wMilliseconds = 12345; + STRINGSW("dddd d MMMM yyyy","Wednesday 23 October 2002"); /* Incorrect DOW and time */ + ret = pGetDateFormatEx(localeW, 0, &curtime, input, buffer, COUNTOF(buffer), NULL); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + curtime.wYear = 2002; + curtime.wMonth = 10; + curtime.wDay = 23; + curtime.wDayOfWeek = 45612; /* Should be 3 - Wednesday */ + curtime.wHour = 65432; /* Invalid */ + curtime.wMinute = 34512; /* Invalid */ + curtime.wSecond = 65535; /* Invalid */ + curtime.wMilliseconds = 12345; + STRINGSW("dddd d MMMM yyyy","Wednesday 23 October 2002"); + ret = pGetDateFormatEx(localeW, 0, &curtime, input, buffer, COUNTOF(buffer), emptyW); /* Use reserved arg */ + ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); + + /* Limit tests */ + + curtime.wYear = 1601; + curtime.wMonth = 1; + curtime.wDay = 1; + curtime.wDayOfWeek = 0; /* Irrelevant */ + curtime.wHour = 0; + curtime.wMinute = 0; + curtime.wSecond = 0; + curtime.wMilliseconds = 0; + STRINGSW("dddd d MMMM yyyy","Monday 1 January 1601"); + SetLastError(0xdeadbeef); + ret = pGetDateFormatEx(localeW, 0, &curtime, input, buffer, COUNTOF(buffer), NULL); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + curtime.wYear = 1600; + curtime.wMonth = 12; + curtime.wDay = 31; + curtime.wDayOfWeek = 0; /* Irrelevant */ + curtime.wHour = 23; + curtime.wMinute = 59; + curtime.wSecond = 59; + curtime.wMilliseconds = 999; + STRINGSW("dddd d MMMM yyyy","Friday 31 December 1600"); + SetLastError(0xdeadbeef); + ret = pGetDateFormatEx(localeW, 0, &curtime, input, buffer, COUNTOF(buffer), NULL); + ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); +} + static void test_GetDateFormatW(void) { int ret; @@ -4003,7 +4301,9 @@ START_TEST(locale) test_GetLocaleInfoW(); test_GetLocaleInfoEx(); test_GetTimeFormatA(); + test_GetTimeFormatEx(); test_GetDateFormatA(); + test_GetDateFormatEx(); test_GetDateFormatW(); test_GetCurrencyFormatA(); /* Also tests the W version */ test_GetNumberFormatA(); /* Also tests the W version */ diff --git a/include/winnls.h b/include/winnls.h index 58344b478c8..3dacc09f120 100644 --- a/include/winnls.h +++ b/include/winnls.h @@ -827,6 +827,7 @@ WINBASEAPI INT WINAPI GetCurrencyFormatA(LCID,DWORD,LPCSTR,const CURRENC WINBASEAPI INT WINAPI GetCurrencyFormatW(LCID,DWORD,LPCWSTR,const CURRENCYFMTW*,LPWSTR,INT); #define GetCurrencyFormat WINELIB_NAME_AW(GetCurrencyFormat) WINBASEAPI INT WINAPI GetDateFormatA(LCID,DWORD,const SYSTEMTIME*,LPCSTR,LPSTR,INT); +WINBASEAPI INT WINAPI GetDateFormatEx(LPCWSTR,DWORD,const SYSTEMTIME*,LPCWSTR,LPWSTR,INT,LPCWSTR); WINBASEAPI INT WINAPI GetDateFormatW(LCID,DWORD,const SYSTEMTIME*,LPCWSTR,LPWSTR,INT); #define GetDateFormat WINELIB_NAME_AW(GetDateFormat) WINBASEAPI INT WINAPI GetGeoInfoA(GEOID,GEOTYPE,LPSTR,INT,LANGID); @@ -850,6 +851,7 @@ WINBASEAPI LCID WINAPI GetSystemDefaultLCID(void); WINBASEAPI LANGID WINAPI GetSystemDefaultUILanguage(void); WINBASEAPI LCID WINAPI GetThreadLocale(void); WINBASEAPI INT WINAPI GetTimeFormatA(LCID,DWORD,const SYSTEMTIME*,LPCSTR,LPSTR,INT); +WINBASEAPI INT WINAPI GetTimeFormatEx(LPCWSTR,DWORD,const SYSTEMTIME*,LPCWSTR,LPWSTR,INT); WINBASEAPI INT WINAPI GetTimeFormatW(LCID,DWORD,const SYSTEMTIME*,LPCWSTR,LPWSTR,INT); #define GetTimeFormat WINELIB_NAME_AW(GetTimeFormat) WINBASEAPI LANGID WINAPI GetUserDefaultLangID(void);