Signed-off-by: Jeff Smith whydoubt@gmail.com --- dlls/msvcrt/tests/time.c | 209 ++++++++++++--------------------------- 1 file changed, 63 insertions(+), 146 deletions(-)
diff --git a/dlls/msvcrt/tests/time.c b/dlls/msvcrt/tests/time.c index cf79e5971e..c8acd848bf 100644 --- a/dlls/msvcrt/tests/time.c +++ b/dlls/msvcrt/tests/time.c @@ -588,13 +588,59 @@ static void test_daylight(void)
static void test_strftime(void) { + const struct { + const char *format; + } tests_einval[] = { + {"%C"}, + {"%D"}, + {"%e"}, + {"%F"}, + {"%h"}, + {"%n"}, + {"%R"}, + {"%t"}, + {"%T"}, + {"%u"}, + }; + + const struct { + const char *format; + const char *ret; + struct tm tm; + BOOL todo; + } tests[] = { + {"e%#%e", "e%e", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }}, + {"%c", "01/01/70 00:00:00", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }}, + {"%c", "02/30/70 00:00:00", { 0, 0, 0, 30, 1, 70, 4, 0, 0 }, TRUE}, + {"%x", "01/01/70", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }}, + {"%X", "00:00:00", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }}, + {"%a", "Thu", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }}, + {"%A", "Thursday", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }}, + {"%b", "Jan", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }}, + {"%B", "January", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }}, + {"%d", "01", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }}, + {"%#d", "1", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }}, + {"%H", "00", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }}, + {"%I", "12", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }}, + {"%j", "001", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }}, + {"%m", "01", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }}, + {"%#M", "0", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }}, + {"%p", "AM", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }}, + {"%U", "00", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }}, + {"%W", "00", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }}, + {"%U", "01", { 0, 0, 0, 1, 0, 70, 0, 0, 0 }}, + {"%W", "00", { 0, 0, 0, 1, 0, 70, 0, 0, 0 }}, + {"%U", "53", { 0, 0, 0, 1, 0, 70, 0, 365, 0 }}, + {"%W", "52", { 0, 0, 0, 1, 0, 70, 0, 365, 0 }}, + }; + static const wchar_t cW[] = { '%','c',0 }; - static const char expected[] = "01/01/70 00:00:00"; time_t gmt; struct tm* gmt_tm; char buf[256], bufA[256]; WCHAR bufW[256]; long retA, retW; + int i;
if (!p_strftime || !p_wcsftime || !p_gmtime) { @@ -608,55 +654,13 @@ static void test_strftime(void) gmt_tm = p_gmtime(&gmt); ok(gmt_tm != NULL, "gmtime failed\n");
- errno = 0xdeadbeef; - retA = p_strftime(bufA, 256, "%C", gmt_tm); - ok(retA == 0, "expected 0, got %ld\n", retA); - ok(errno==EINVAL || broken(errno==0xdeadbeef), "errno = %d\n", errno); - - errno = 0xdeadbeef; - retA = p_strftime(bufA, 256, "%D", gmt_tm); - ok(retA == 0, "expected 0, got %ld\n", retA); - ok(errno==EINVAL || broken(errno==0xdeadbeef), "errno = %d\n", errno); - - errno = 0xdeadbeef; - retA = p_strftime(bufA, 256, "%e", gmt_tm); - ok(retA == 0, "expected 0, got %ld\n", retA); - ok(errno==EINVAL || broken(errno==0xdeadbeef), "errno = %d\n", errno); - - errno = 0xdeadbeef; - retA = p_strftime(bufA, 256, "%F", gmt_tm); - ok(retA == 0, "expected 0, got %ld\n", retA); - ok(errno==EINVAL || broken(errno==0xdeadbeef), "errno = %d\n", errno); - - errno = 0xdeadbeef; - retA = p_strftime(bufA, 256, "%h", gmt_tm); - ok(retA == 0, "expected 0, got %ld\n", retA); - ok(errno==EINVAL || broken(errno==0xdeadbeef), "errno = %d\n", errno); - - errno = 0xdeadbeef; - retA = p_strftime(bufA, 256, "%n", gmt_tm); - ok(retA == 0, "expected 0, got %ld\n", retA); - ok(errno==EINVAL || broken(errno==0xdeadbeef), "errno = %d\n", errno); - - errno = 0xdeadbeef; - retA = p_strftime(bufA, 256, "%R", gmt_tm); - ok(retA == 0, "expected 0, got %ld\n", retA); - ok(errno==EINVAL || broken(errno==0xdeadbeef), "errno = %d\n", errno); - - errno = 0xdeadbeef; - retA = p_strftime(bufA, 256, "%t", gmt_tm); - ok(retA == 0, "expected 0, got %ld\n", retA); - ok(errno==EINVAL || broken(errno==0xdeadbeef), "errno = %d\n", errno); - - errno = 0xdeadbeef; - retA = p_strftime(bufA, 256, "%T", gmt_tm); - ok(retA == 0, "expected 0, got %ld\n", retA); - ok(errno==EINVAL || broken(errno==0xdeadbeef), "errno = %d\n", errno); - - errno = 0xdeadbeef; - retA = p_strftime(bufA, 256, "%u", gmt_tm); - ok(retA == 0, "expected 0, got %ld\n", retA); - ok(errno==EINVAL || broken(errno==0xdeadbeef), "errno = %d\n", errno); + for (i=0; i<ARRAY_SIZE(tests_einval); i++) + { + errno = 0xdeadbeef; + retA = p_strftime(bufA, 256, tests_einval[i].format, gmt_tm); + ok(retA == 0, "%d) ret = %ld\n", i, retA); + ok(errno==EINVAL || broken(errno==0xdeadbeef), "%d) errno = %d\n", i, errno); + }
errno = 0xdeadbeef; retA = p_strftime(NULL, 0, "copy", gmt_tm); @@ -691,14 +695,17 @@ static void test_strftime(void) ok(errno == EINVAL, "errno = %d\n", errno); }
- retA = p_strftime(bufA, 256, "e%#%e", gmt_tm); - ok(retA == 3, "expected 3, got %ld\n", retA); - ok(!strcmp(bufA, "e%e"), "got %s\n", bufA); + for (i=0; i<ARRAY_SIZE(tests); i++) + { + retA = p_strftime(bufA, 256, tests[i].format, &tests[i].tm); + todo_wine_if(tests[i].todo) { + ok(retA == strlen(tests[i].ret), "%d) ret = %ld\n", i, retA); + ok(!strcmp(bufA, tests[i].ret), "%d) buf = "%s", expected "%s"\n", + i, bufA, tests[i].ret); + } + }
retA = p_strftime(bufA, 256, "%c", gmt_tm); - ok(retA == 17, "expected 17, got %ld\n", retA); - ok(strcmp(bufA, expected) == 0, "expected %s, got %s\n", expected, bufA); - retW = p_wcsftime(bufW, 256, cW, gmt_tm); ok(retW == 17, "expected 17, got %ld\n", retW); ok(retA == retW, "expected %ld, got %ld\n", retA, retW); @@ -707,96 +714,6 @@ static void test_strftime(void) buf[retA] = 0; ok(strcmp(bufA, buf) == 0, "expected %s, got %s\n", bufA, buf);
- retA = p_strftime(bufA, 256, "%x", gmt_tm); - ok(retA == 8, "expected 8, got %ld\n", retA); - ok(!strcmp(bufA, "01/01/70"), "got %s\n", bufA); - - retA = p_strftime(bufA, 256, "%X", gmt_tm); - ok(retA == 8, "expected 8, got %ld\n", retA); - ok(!strcmp(bufA, "00:00:00"), "got %s\n", bufA); - - retA = p_strftime(bufA, 256, "%a", gmt_tm); - ok(retA == 3, "expected 3, got %ld\n", retA); - ok(!strcmp(bufA, "Thu"), "got %s\n", bufA); - - retA = p_strftime(bufA, 256, "%A", gmt_tm); - ok(retA == 8, "expected 8, got %ld\n", retA); - ok(!strcmp(bufA, "Thursday"), "got %s\n", bufA); - - retA = p_strftime(bufA, 256, "%b", gmt_tm); - ok(retA == 3, "expected 3, got %ld\n", retA); - ok(!strcmp(bufA, "Jan"), "got %s\n", bufA); - - retA = p_strftime(bufA, 256, "%B", gmt_tm); - ok(retA == 7, "expected 7, got %ld\n", retA); - ok(!strcmp(bufA, "January"), "got %s\n", bufA); - - retA = p_strftime(bufA, 256, "%d", gmt_tm); - ok(retA == 2, "expected 2, got %ld\n", retA); - ok(!strcmp(bufA, "01"), "got %s\n", bufA); - - retA = p_strftime(bufA, 256, "%#d", gmt_tm); - ok(retA == 1, "expected 1, got %ld\n", retA); - ok(!strcmp(bufA, "1"), "got %s\n", bufA); - - retA = p_strftime(bufA, 256, "%H", gmt_tm); - ok(retA == 2, "expected 2, got %ld\n", retA); - ok(!strcmp(bufA, "00"), "got %s\n", bufA); - - retA = p_strftime(bufA, 256, "%I", gmt_tm); - ok(retA == 2, "expected 2, got %ld\n", retA); - ok(!strcmp(bufA, "12"), "got %s\n", bufA); - - retA = p_strftime(bufA, 256, "%j", gmt_tm); - ok(retA == 3, "expected 3, got %ld\n", retA); - ok(!strcmp(bufA, "001"), "got %s\n", bufA); - - retA = p_strftime(bufA, 256, "%m", gmt_tm); - ok(retA == 2, "expected 2, got %ld\n", retA); - ok(!strcmp(bufA, "01"), "got %s\n", bufA); - - retA = p_strftime(bufA, 256, "%#M", gmt_tm); - ok(retA == 1, "expected 1, got %ld\n", retA); - ok(!strcmp(bufA, "0"), "got %s\n", bufA); - - retA = p_strftime(bufA, 256, "%p", gmt_tm); - ok(retA == 2, "expected 2, got %ld\n", retA); - ok(!strcmp(bufA, "AM"), "got %s\n", bufA); - - retA = p_strftime(bufA, 256, "%U", gmt_tm); - ok(retA == 2, "expected 2, got %ld\n", retA); - ok(!strcmp(bufA, "00"), "got %s\n", bufA); - - retA = p_strftime(bufA, 256, "%W", gmt_tm); - ok(retA == 2, "expected 2, got %ld\n", retA); - ok(!strcmp(bufA, "00"), "got %s\n", bufA); - - gmt_tm->tm_wday = 0; - retA = p_strftime(bufA, 256, "%U", gmt_tm); - ok(retA == 2, "expected 2, got %ld\n", retA); - ok(!strcmp(bufA, "01"), "got %s\n", bufA); - - retA = p_strftime(bufA, 256, "%W", gmt_tm); - ok(retA == 2, "expected 2, got %ld\n", retA); - ok(!strcmp(bufA, "00"), "got %s\n", bufA); - - gmt_tm->tm_yday = 365; - retA = p_strftime(bufA, 256, "%U", gmt_tm); - ok(retA == 2, "expected 2, got %ld\n", retA); - ok(!strcmp(bufA, "53"), "got %s\n", bufA); - - retA = p_strftime(bufA, 256, "%W", gmt_tm); - ok(retA == 2, "expected 2, got %ld\n", retA); - ok(!strcmp(bufA, "52"), "got %s\n", bufA); - - gmt_tm->tm_mon = 1; - gmt_tm->tm_mday = 30; - retA = p_strftime(bufA, 256, "%c", gmt_tm); - todo_wine { - ok(retA == 17, "expected 17, got %ld\n", retA); - ok(!strcmp(bufA, "02/30/70 00:00:00"), "got %s\n", bufA); - } - if(!setlocale(LC_ALL, "Japanese_Japan.932")) { win_skip("Japanese_Japan.932 locale not available\n"); return;
Signed-off-by: Jeff Smith whydoubt@gmail.com --- dlls/msvcrt/tests/time.c | 6 ++++++ dlls/ucrtbase/tests/misc.c | 11 +++++++++++ 2 files changed, 17 insertions(+)
diff --git a/dlls/msvcrt/tests/time.c b/dlls/msvcrt/tests/time.c index c8acd848bf..4a3d81d2dd 100644 --- a/dlls/msvcrt/tests/time.c +++ b/dlls/msvcrt/tests/time.c @@ -612,8 +612,14 @@ static void test_strftime(void) {"e%#%e", "e%e", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }}, {"%c", "01/01/70 00:00:00", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }}, {"%c", "02/30/70 00:00:00", { 0, 0, 0, 30, 1, 70, 4, 0, 0 }, TRUE}, + {"%#c", "Thursday, January 01, 1970 00:00:00", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }}, + {"%#c", "Thursday, February 30, 1970 00:00:00", { 0, 0, 0, 30, 1, 70, 4, 0, 0 }, TRUE}, {"%x", "01/01/70", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }}, + {"%x", "02/30/70", { 0, 0, 0, 30, 1, 70, 4, 0, 0 }, TRUE}, + {"%#x", "Thursday, January 01, 1970", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }}, + {"%#x", "Thursday, February 30, 1970", { 0, 0, 0, 30, 1, 70, 4, 0, 0 }, TRUE}, {"%X", "00:00:00", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }}, + {"%X", "14:00:00", { 0, 0, 14, 1, 0, 70, 4, 0, 0 }}, {"%a", "Thu", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }}, {"%A", "Thursday", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }}, {"%b", "Jan", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }}, diff --git a/dlls/ucrtbase/tests/misc.c b/dlls/ucrtbase/tests/misc.c index fb138a0fa4..fa5f29372c 100644 --- a/dlls/ucrtbase/tests/misc.c +++ b/dlls/ucrtbase/tests/misc.c @@ -936,6 +936,17 @@ static void test_strftime(void) {"%g", "72", { 0, 0, 0, 3, 0, 72, 1, 2, 0 }}, {"%G", "1971", { 0, 0, 0, 2, 0, 72, 0, 1, 0 }}, {"%G", "1972", { 0, 0, 0, 3, 0, 72, 1, 2, 0 }}, + {"%c", "Thu Jan 1 00:00:00 1970", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }, TRUE}, + {"%c", "Thu Feb 30 00:00:00 1970", { 0, 0, 0, 30, 1, 70, 4, 0, 0 }, TRUE}, + {"%#c", "Thursday, January 01, 1970 00:00:00", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }}, + {"%#c", "Thursday, February 30, 1970 00:00:00", { 0, 0, 0, 30, 1, 70, 4, 0, 0 }, TRUE}, + {"%x", "01/01/70", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }}, + {"%x", "02/30/70", { 0, 0, 0, 30, 1, 70, 4, 0, 0 }, TRUE}, + {"%#x", "Thursday, January 01, 1970", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }}, + {"%#x", "Thursday, February 30, 1970", { 0, 0, 0, 30, 1, 70, 4, 0, 0 }, TRUE}, + {"%X", "00:00:00", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }}, + {"%X", "14:00:00", { 0, 0, 14, 1, 0, 70, 4, 0, 0 }}, + {"%X", "23:59:60", { 60, 59, 23, 1, 0, 70, 4, 0, 0 }, TRUE}, };
const struct tm epoch = { 0, 0, 0, 1, 0, 70, 4, 0, 0 };
Signed-off-by: Piotr Caban piotr@codeweavers.com
Signed-off-by: Jeff Smith whydoubt@gmail.com --- dlls/msvcrt/tests/time.c | 8 +-- dlls/msvcrt/time.c | 131 ++++++++++++++++++++++++------------- dlls/ucrtbase/tests/misc.c | 6 +- 3 files changed, 94 insertions(+), 51 deletions(-)
diff --git a/dlls/msvcrt/tests/time.c b/dlls/msvcrt/tests/time.c index 4a3d81d2dd..a71c3efee2 100644 --- a/dlls/msvcrt/tests/time.c +++ b/dlls/msvcrt/tests/time.c @@ -611,13 +611,13 @@ static void test_strftime(void) } tests[] = { {"e%#%e", "e%e", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }}, {"%c", "01/01/70 00:00:00", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }}, - {"%c", "02/30/70 00:00:00", { 0, 0, 0, 30, 1, 70, 4, 0, 0 }, TRUE}, + {"%c", "02/30/70 00:00:00", { 0, 0, 0, 30, 1, 70, 4, 0, 0 }}, {"%#c", "Thursday, January 01, 1970 00:00:00", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }}, - {"%#c", "Thursday, February 30, 1970 00:00:00", { 0, 0, 0, 30, 1, 70, 4, 0, 0 }, TRUE}, + {"%#c", "Thursday, February 30, 1970 00:00:00", { 0, 0, 0, 30, 1, 70, 4, 0, 0 }}, {"%x", "01/01/70", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }}, - {"%x", "02/30/70", { 0, 0, 0, 30, 1, 70, 4, 0, 0 }, TRUE}, + {"%x", "02/30/70", { 0, 0, 0, 30, 1, 70, 4, 0, 0 }}, {"%#x", "Thursday, January 01, 1970", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }}, - {"%#x", "Thursday, February 30, 1970", { 0, 0, 0, 30, 1, 70, 4, 0, 0 }, TRUE}, + {"%#x", "Thursday, February 30, 1970", { 0, 0, 0, 30, 1, 70, 4, 0, 0 }}, {"%X", "00:00:00", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }}, {"%X", "14:00:00", { 0, 0, 14, 1, 0, 70, 4, 0, 0 }}, {"%a", "Thu", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }}, diff --git a/dlls/msvcrt/time.c b/dlls/msvcrt/time.c index ae6fcc8b7a..058a8a53c9 100644 --- a/dlls/msvcrt/time.c +++ b/dlls/msvcrt/time.c @@ -954,46 +954,6 @@ char ** CDECL __p__tzname(void) return MSVCRT__tzname; }
-static inline BOOL strftime_date(char *str, MSVCRT_size_t *pos, MSVCRT_size_t max, - BOOL alternate, const struct MSVCRT_tm *mstm, MSVCRT___lc_time_data *time_data) -{ - char *format; - SYSTEMTIME st; - MSVCRT_size_t ret; - LCID lcid; - - st.wYear = mstm->tm_year + 1900; - st.wMonth = mstm->tm_mon + 1; - st.wDayOfWeek = mstm->tm_wday; - st.wDay = mstm->tm_mday; - st.wHour = mstm->tm_hour; - st.wMinute = mstm->tm_min; - st.wSecond = mstm->tm_sec; - st.wMilliseconds = 0; - -#if _MSVCR_VER < 110 - lcid = time_data->lcid; -#else - lcid = LocaleNameToLCID(time_data->locname, 0); -#endif - - format = alternate ? time_data->str.names.date : time_data->str.names.short_date; - ret = GetDateFormatA(lcid, 0, &st, format, NULL, 0); - if(ret && ret<max-*pos) - ret = GetDateFormatA(lcid, 0, &st, format, str+*pos, max-*pos); - if(!ret) { - *str = 0; - *MSVCRT__errno() = MSVCRT_EINVAL; - return FALSE; - }else if(ret > max-*pos) { - *str = 0; - *MSVCRT__errno() = MSVCRT_ERANGE; - return FALSE; - } - *pos += ret-1; - return TRUE; -} - static inline BOOL strftime_time(char *str, MSVCRT_size_t *pos, MSVCRT_size_t max, const struct MSVCRT_tm *mstm, MSVCRT___lc_time_data *time_data) { @@ -1057,9 +1017,12 @@ static inline BOOL strftime_tzdiff(char *str, MSVCRT_size_t *pos, MSVCRT_size_t return TRUE; }
-static inline BOOL strftime_str(char *str, MSVCRT_size_t *pos, MSVCRT_size_t max, char *src) +#define strftime_str(a,b,c,d) strftime_nstr(a,b,c,d,MSVCRT_SIZE_MAX) +static inline BOOL strftime_nstr(char *str, MSVCRT_size_t *pos, MSVCRT_size_t max, + const char *src, MSVCRT_size_t len) { - MSVCRT_size_t len = strlen(src); + if(len == MSVCRT_SIZE_MAX) + len = strlen(src); if(len > max-*pos) { *str = 0; *MSVCRT__errno() = MSVCRT_ERANGE; @@ -1093,6 +1056,84 @@ static inline BOOL strftime_int(char *str, MSVCRT_size_t *pos, MSVCRT_size_t max return TRUE; }
+static inline BOOL strftime_format(char *str, MSVCRT_size_t *pos, MSVCRT_size_t max, + const struct MSVCRT_tm *mstm, MSVCRT___lc_time_data *time_data, const char *format) +{ + BOOL ret = TRUE; + + while(*format && ret) + { + const char *substr = format++; + switch(*substr) { + case ''': + while(*format && *format != ''') format++; + substr++; + ret = strftime_nstr(str, pos, max, substr, (*substr == ''') ? 1 : format - substr); + if(ret && *format == ''') + format++; + break; + case 'd': + while(*format == *substr) format++; + if(format - substr < 3) + ret = strftime_int(str, pos, max, mstm->tm_mday, (format - substr < 2) ? 0 : 2, 0, 31); + else { + if(!MSVCRT_CHECK_PMT(mstm->tm_wday>=0 && mstm->tm_wday<=6)) + goto einval_error; + if(format - substr == 3) + ret = strftime_str(str, pos, max, time_data->str.names.short_wday[mstm->tm_wday]); + else + ret = strftime_str(str, pos, max, time_data->str.names.wday[mstm->tm_wday]); + } + break; + case 'M': + while(*format == *substr) format++; + if(format - substr < 3) + ret = strftime_int(str, pos, max, mstm->tm_mon+1, (format - substr < 2) ? 0 : 2, 1, 12); + else { + if(!MSVCRT_CHECK_PMT(mstm->tm_mon>=0 && mstm->tm_mon<=11)) + goto einval_error; + if(format - substr == 3) + ret = strftime_str(str, pos, max, time_data->str.names.short_mon[mstm->tm_mon]); + else + ret = strftime_str(str, pos, max, time_data->str.names.mon[mstm->tm_mon]); + } + break; + case 'y': + while(*format == *substr) format++; + if(format - substr < 3) { + int year = mstm->tm_year + 1900; +#if _MSVCR_VER>=140 + if(!MSVCRT_CHECK_PMT(year>=0 && year<=9999)) + goto einval_error; +#else + if(!MSVCRT_CHECK_PMT(year>=1900)) + goto einval_error; +#endif + ret = strftime_int(str, pos, max, year%100, 2, 0, 99); + } else + ret = strftime_int(str, pos, max, mstm->tm_year+1900, 4, 0, 9999); + break; + case 'g': + while(*format == *substr) format++; + /* TODO: era */ + break; + default: + while(*format && *format != ''' && + *format != 'd' && *format != 'M' && + *format != 'y' && *format != 'g') + format++; + ret = strftime_nstr(str, pos, max, substr, format - substr); + break; + } + } + + return ret; + +einval_error: + *str = 0; + return FALSE; +} + static MSVCRT_size_t strftime_helper(char *str, MSVCRT_size_t max, const char *format, const struct MSVCRT_tm *mstm, MSVCRT___lc_time_data *time_data, MSVCRT__locale_t loc) { @@ -1136,7 +1177,8 @@ static MSVCRT_size_t strftime_helper(char *str, MSVCRT_size_t max, const char *f
switch(*format) { case 'c': - if(!strftime_date(str, &ret, max, alternate, mstm, time_data)) + if(!strftime_format(str, &ret, max, mstm, time_data, + alternate ? time_data->str.names.date : time_data->str.names.short_date)) return 0; if(ret < max) str[ret++] = ' '; @@ -1144,7 +1186,8 @@ static MSVCRT_size_t strftime_helper(char *str, MSVCRT_size_t max, const char *f return 0; break; case 'x': - if(!strftime_date(str, &ret, max, alternate, mstm, time_data)) + if(!strftime_format(str, &ret, max, mstm, time_data, + alternate ? time_data->str.names.date : time_data->str.names.short_date)) return 0; break; case 'X': diff --git a/dlls/ucrtbase/tests/misc.c b/dlls/ucrtbase/tests/misc.c index fa5f29372c..35b7375cd8 100644 --- a/dlls/ucrtbase/tests/misc.c +++ b/dlls/ucrtbase/tests/misc.c @@ -939,11 +939,11 @@ static void test_strftime(void) {"%c", "Thu Jan 1 00:00:00 1970", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }, TRUE}, {"%c", "Thu Feb 30 00:00:00 1970", { 0, 0, 0, 30, 1, 70, 4, 0, 0 }, TRUE}, {"%#c", "Thursday, January 01, 1970 00:00:00", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }}, - {"%#c", "Thursday, February 30, 1970 00:00:00", { 0, 0, 0, 30, 1, 70, 4, 0, 0 }, TRUE}, + {"%#c", "Thursday, February 30, 1970 00:00:00", { 0, 0, 0, 30, 1, 70, 4, 0, 0 }}, {"%x", "01/01/70", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }}, - {"%x", "02/30/70", { 0, 0, 0, 30, 1, 70, 4, 0, 0 }, TRUE}, + {"%x", "02/30/70", { 0, 0, 0, 30, 1, 70, 4, 0, 0 }}, {"%#x", "Thursday, January 01, 1970", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }}, - {"%#x", "Thursday, February 30, 1970", { 0, 0, 0, 30, 1, 70, 4, 0, 0 }, TRUE}, + {"%#x", "Thursday, February 30, 1970", { 0, 0, 0, 30, 1, 70, 4, 0, 0 }}, {"%X", "00:00:00", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }}, {"%X", "14:00:00", { 0, 0, 14, 1, 0, 70, 4, 0, 0 }}, {"%X", "23:59:60", { 60, 59, 23, 1, 0, 70, 4, 0, 0 }, TRUE},
Hi Jeff,
I've sent patches that are adding more tests for these formats. I've also changed this patch and sent it as part of the serie. I didn't change patch 4/5 - it will need to be reworked in similar way as this patch to pass the tests.
The tests also show that there's something wrong with patch 5/5. I didn't dig into it.
Thanks, Piotr
Hi Piotr,
Sounds good, and I will take a good look at it shortly. BTW, great work hooking into _Strftime to test these! It definitely exposed those aspects that differ from GetTimeFormat/GetDateFormat even more than I had determined. It is interesting that there seems to be no way to have a single-quote in the final string, and even the single-quote in the format string doesn't work as expected in some versions of ucrtbase.dll.
Thanks, Jeff
On Wed, Nov 20, 2019 at 6:21 AM Piotr Caban piotr.caban@gmail.com wrote:
Hi Jeff,
I've sent patches that are adding more tests for these formats. I've also changed this patch and sent it as part of the serie. I didn't change patch 4/5 - it will need to be reworked in similar way as this patch to pass the tests.
The tests also show that there's something wrong with patch 5/5. I didn't dig into it.
Thanks, Piotr
Signed-off-by: Jeff Smith whydoubt@gmail.com --- dlls/msvcrt/time.c | 86 ++++++++++++++++++-------------------- dlls/ucrtbase/tests/misc.c | 2 +- 2 files changed, 42 insertions(+), 46 deletions(-)
diff --git a/dlls/msvcrt/time.c b/dlls/msvcrt/time.c index 058a8a53c9..30f59e0846 100644 --- a/dlls/msvcrt/time.c +++ b/dlls/msvcrt/time.c @@ -54,6 +54,12 @@ static const int MonthLengths[2][12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } };
+#if _MSVCR_VER>=140 +static const int MAX_SECONDS = 60; +#else +static const int MAX_SECONDS = 59; +#endif + static inline BOOL IsLeapYear(int Year) { return Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0); @@ -954,45 +960,6 @@ char ** CDECL __p__tzname(void) return MSVCRT__tzname; }
-static inline BOOL strftime_time(char *str, MSVCRT_size_t *pos, MSVCRT_size_t max, - const struct MSVCRT_tm *mstm, MSVCRT___lc_time_data *time_data) -{ - SYSTEMTIME st; - MSVCRT_size_t ret; - LCID lcid; - - st.wYear = mstm->tm_year + 1900; - st.wMonth = mstm->tm_mon + 1; - st.wDayOfWeek = mstm->tm_wday; - st.wDay = mstm->tm_mday; - st.wHour = mstm->tm_hour; - st.wMinute = mstm->tm_min; - st.wSecond = mstm->tm_sec; - st.wMilliseconds = 0; - -#if _MSVCR_VER < 110 - lcid = time_data->lcid; -#else - lcid = LocaleNameToLCID(time_data->locname, 0); -#endif - - ret = GetTimeFormatA(lcid, 0, &st, time_data->str.names.time, NULL, 0); - if(ret && ret<max-*pos) - ret = GetTimeFormatA(lcid, 0, &st, time_data->str.names.time, - str+*pos, max-*pos); - if(!ret) { - *str = 0; - *MSVCRT__errno() = MSVCRT_EINVAL; - return FALSE; - }else if(ret > max-*pos) { - *str = 0; - *MSVCRT__errno() = MSVCRT_ERANGE; - return FALSE; - } - *pos += ret-1; - return TRUE; -} - static inline BOOL strftime_tzdiff(char *str, MSVCRT_size_t *pos, MSVCRT_size_t max, BOOL is_dst) { MSVCRT_long tz = MSVCRT___timezone + (is_dst ? MSVCRT__dstbias : 0); @@ -1117,10 +1084,39 @@ static inline BOOL strftime_format(char *str, MSVCRT_size_t *pos, MSVCRT_size_t while(*format == *substr) format++; /* TODO: era */ break; + case 'h': + while(*format == *substr) format++; + if(!MSVCRT_CHECK_PMT(mstm->tm_hour>=0 && mstm->tm_hour<=23)) + goto einval_error; + ret = strftime_int(str, pos, max, (mstm->tm_hour + 11) % 12 + 1, + (format - substr < 2) ? 0 : 2, 1, 12); + break; + case 'H': + while(*format == *substr) format++; + ret = strftime_int(str, pos, max, mstm->tm_hour, (format - substr < 2) ? 0 : 2, 0, 23); + break; + case 'm': + while(*format == *substr) format++; + ret = strftime_int(str, pos, max, mstm->tm_min, (format - substr < 2) ? 0 : 2, 0, 59); + break; + case 's': + while(*format == *substr) format++; + ret = strftime_int(str, pos, max, mstm->tm_sec, + (format - substr < 2) ? 0 : 2, 0, MAX_SECONDS); + break; + case 't': + while(*format == *substr) format++; + if(!MSVCRT_CHECK_PMT(mstm->tm_hour>=0 && mstm->tm_hour<=23)) + goto einval_error; + ret = strftime_nstr(str, pos, max, + (mstm->tm_hour < 12) ? time_data->str.names.am : time_data->str.names.pm, + (format - substr < 2) ? 1 : MSVCRT_SIZE_MAX); + break; default: while(*format && *format != ''' && - *format != 'd' && *format != 'M' && - *format != 'y' && *format != 'g') + *format != 'd' && *format != 'M' && *format != 'y' && + *format != 'g' && *format != 'h' && *format != 'H' && + *format != 'm' && *format != 's' && *format != 't') format++; ret = strftime_nstr(str, pos, max, substr, format - substr); break; @@ -1182,7 +1178,7 @@ static MSVCRT_size_t strftime_helper(char *str, MSVCRT_size_t max, const char *f return 0; if(ret < max) str[ret++] = ' '; - if(!strftime_time(str, &ret, max, mstm, time_data)) + if(!strftime_format(str, &ret, max, mstm, time_data, time_data->str.names.time)) return 0; break; case 'x': @@ -1191,7 +1187,7 @@ static MSVCRT_size_t strftime_helper(char *str, MSVCRT_size_t max, const char *f return 0; break; case 'X': - if(!strftime_time(str, &ret, max, mstm, time_data)) + if(!strftime_format(str, &ret, max, mstm, time_data, time_data->str.names.time)) return 0; break; case 'a': @@ -1329,7 +1325,7 @@ static MSVCRT_size_t strftime_helper(char *str, MSVCRT_size_t max, const char *f break; #endif case 'S': - if(!strftime_int(str, &ret, max, mstm->tm_sec, alternate ? 0 : 2, 0, 59)) + if(!strftime_int(str, &ret, max, mstm->tm_sec, alternate ? 0 : 2, 0, MAX_SECONDS)) return 0; break; #if _MSVCR_VER>=140 @@ -1345,7 +1341,7 @@ static MSVCRT_size_t strftime_helper(char *str, MSVCRT_size_t max, const char *f return 0; if(ret < max) str[ret++] = ':'; - if(!strftime_int(str, &ret, max, mstm->tm_sec, alternate ? 0 : 2, 0, 59)) + if(!strftime_int(str, &ret, max, mstm->tm_sec, alternate ? 0 : 2, 0, MAX_SECONDS)) return 0; break; case 'u': diff --git a/dlls/ucrtbase/tests/misc.c b/dlls/ucrtbase/tests/misc.c index 35b7375cd8..3aca31734f 100644 --- a/dlls/ucrtbase/tests/misc.c +++ b/dlls/ucrtbase/tests/misc.c @@ -946,7 +946,7 @@ static void test_strftime(void) {"%#x", "Thursday, February 30, 1970", { 0, 0, 0, 30, 1, 70, 4, 0, 0 }}, {"%X", "00:00:00", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }}, {"%X", "14:00:00", { 0, 0, 14, 1, 0, 70, 4, 0, 0 }}, - {"%X", "23:59:60", { 60, 59, 23, 1, 0, 70, 4, 0, 0 }, TRUE}, + {"%X", "23:59:60", { 60, 59, 23, 1, 0, 70, 4, 0, 0 }}, };
const struct tm epoch = { 0, 0, 0, 1, 0, 70, 4, 0, 0 };
Signed-off-by: Jeff Smith whydoubt@gmail.com --- dlls/msvcrt/time.c | 33 ++++++++++++++++++++++++--------- dlls/ucrtbase/tests/misc.c | 4 ++-- 2 files changed, 26 insertions(+), 11 deletions(-)
diff --git a/dlls/msvcrt/time.c b/dlls/msvcrt/time.c index 30f59e0846..3d27f75506 100644 --- a/dlls/msvcrt/time.c +++ b/dlls/msvcrt/time.c @@ -1135,8 +1135,9 @@ static MSVCRT_size_t strftime_helper(char *str, MSVCRT_size_t max, const char *f { MSVCRT_size_t ret, tmp; BOOL alternate; + MSVCRT_pthreadlocinfo locinfo;
- TRACE("(%p %ld %s %p %p)\n", str, max, format, mstm, time_data); + TRACE("(%p %ld %s %p %p %p)\n", str, max, format, mstm, time_data, loc);
if(!str || !format) { if(str && max) @@ -1145,8 +1146,9 @@ static MSVCRT_size_t strftime_helper(char *str, MSVCRT_size_t max, const char *f return 0; }
+ locinfo = loc ? loc->locinfo : get_locinfo(); if(!time_data) - time_data = loc ? loc->locinfo->lc_time_curr : get_locinfo()->lc_time_curr; + time_data = locinfo->lc_time_curr;
for(ret=0; *format && ret<max; format++) { if(*format != '%') { @@ -1173,13 +1175,26 @@ static MSVCRT_size_t strftime_helper(char *str, MSVCRT_size_t max, const char *f
switch(*format) { case 'c': - if(!strftime_format(str, &ret, max, mstm, time_data, - alternate ? time_data->str.names.date : time_data->str.names.short_date)) - return 0; - if(ret < max) - str[ret++] = ' '; - if(!strftime_format(str, &ret, max, mstm, time_data, time_data->str.names.time)) - return 0; +#if _MSVCR_VER>=140 + if (!strcmp(locinfo->lc_category[MSVCRT_LC_TIME].locale, "C") && !alternate) + { + MSVCRT_size_t day_offset = ret + 8; + if(!strftime_format(str, &ret, max, mstm, time_data, "ddd MMM dd HH:mm:ss yyyy")) + return 0; + if(str[day_offset] == '0') + str[day_offset] = ' '; + } + else +#endif + { + if(!strftime_format(str, &ret, max, mstm, time_data, + alternate ? time_data->str.names.date : time_data->str.names.short_date)) + return 0; + if(ret < max) + str[ret++] = ' '; + if(!strftime_format(str, &ret, max, mstm, time_data, time_data->str.names.time)) + return 0; + } break; case 'x': if(!strftime_format(str, &ret, max, mstm, time_data, diff --git a/dlls/ucrtbase/tests/misc.c b/dlls/ucrtbase/tests/misc.c index 3aca31734f..e4170faef3 100644 --- a/dlls/ucrtbase/tests/misc.c +++ b/dlls/ucrtbase/tests/misc.c @@ -936,8 +936,8 @@ static void test_strftime(void) {"%g", "72", { 0, 0, 0, 3, 0, 72, 1, 2, 0 }}, {"%G", "1971", { 0, 0, 0, 2, 0, 72, 0, 1, 0 }}, {"%G", "1972", { 0, 0, 0, 3, 0, 72, 1, 2, 0 }}, - {"%c", "Thu Jan 1 00:00:00 1970", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }, TRUE}, - {"%c", "Thu Feb 30 00:00:00 1970", { 0, 0, 0, 30, 1, 70, 4, 0, 0 }, TRUE}, + {"%c", "Thu Jan 1 00:00:00 1970", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }}, + {"%c", "Thu Feb 30 00:00:00 1970", { 0, 0, 0, 30, 1, 70, 4, 0, 0 }}, {"%#c", "Thursday, January 01, 1970 00:00:00", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }}, {"%#c", "Thursday, February 30, 1970 00:00:00", { 0, 0, 0, 30, 1, 70, 4, 0, 0 }}, {"%x", "01/01/70", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }},