From 262991b6416f314049aa27ddf27a3e0b8ccda89e Mon Sep 17 00:00:00 2001 From: Vladislav Shchapov Date: Thu, 22 Dec 2022 20:25:12 +0500 Subject: [PATCH 1/3] Set timezone for chrono tests Signed-off-by: Vladislav Shchapov --- .github/workflows/linux.yml | 3 +++ .github/workflows/macos.yml | 3 +++ .github/workflows/windows.yml | 6 ++++++ 3 files changed, 12 insertions(+) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 6818eafadf56..4d0acb14bb63 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -59,6 +59,9 @@ jobs: steps: - uses: actions/checkout@v3 + - name: Set timezone + run: sudo timedatectl set-timezone 'Asia/Yekaterinburg' + - name: Add repositories for older GCC run: | # Bellow two repos provide GCC 4.8, 5.5 and 6.4 diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index e76324d8e76c..475dc77fc4cf 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -18,6 +18,9 @@ jobs: steps: - uses: actions/checkout@v3 + - name: Set timezone + run: sudo systemsetup -settimezone 'Asia/Yekaterinburg' + - name: Create Build Environment run: cmake -E make_directory ${{runner.workspace}}/build diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 9bef6e8b9d26..d48b13d03d3c 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -43,6 +43,9 @@ jobs: steps: - uses: actions/checkout@v3 + - name: Set timezone + run: tzutil /s "Ekaterinburg Standard Time" + - name: Create Build Environment run: cmake -E make_directory ${{runner.workspace}}/build @@ -77,6 +80,9 @@ jobs: matrix: sys: [ mingw64, ucrt64 ] steps: + - name: Set timezone + run: tzutil /s "Ekaterinburg Standard Time" + shell: cmd - uses: msys2/setup-msys2@v2 with: release: false From fd0e101869e1764f9bd638fcc95b002724de1580 Mon Sep 17 00:00:00 2001 From: Vladislav Shchapov Date: Thu, 22 Dec 2022 20:39:19 +0500 Subject: [PATCH 2/3] Improve timezone tests Signed-off-by: Vladislav Shchapov --- test/chrono-test.cc | 54 ++++++++++++++++++++++++++++++++++++--------- test/xchar-test.cc | 51 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 92 insertions(+), 13 deletions(-) diff --git a/test/chrono-test.cc b/test/chrono-test.cc index f986c203847f..d4947005c057 100644 --- a/test/chrono-test.cc +++ b/test/chrono-test.cc @@ -264,15 +264,15 @@ TEST(chrono_test, system_clock_time_point) { "%OU", "%W", "%OW", "%V", "%OV", "%j", "%d", "%Od", "%e", "%Oe", "%a", "%A", "%w", "%Ow", "%u", "%Ou", "%H", "%OH", "%I", "%OI", "%M", "%OM", "%S", "%OS", "%x", "%Ex", "%X", - "%EX", "%D", "%F", "%R", "%T", "%p", "%z", "%Z"}; + "%EX", "%D", "%F", "%R", "%T", "%p"}; #ifndef _WIN32 // Disabled on Windows because these formats are not consistent among // platforms. spec_list.insert(spec_list.end(), {"%c", "%Ec", "%r"}); #elif defined(__MINGW32__) && !defined(_UCRT) // Only C89 conversion specifiers when using MSVCRT instead of UCRT - spec_list = {"%%", "%Y", "%y", "%b", "%B", "%m", "%U", "%W", "%j", "%d", "%a", - "%A", "%w", "%H", "%I", "%M", "%S", "%x", "%X", "%p", "%Z"}; + spec_list = {"%%", "%Y", "%y", "%b", "%B", "%m", "%U", "%W", "%j", "%d", + "%a", "%A", "%w", "%H", "%I", "%M", "%S", "%x", "%X", "%p"}; #endif spec_list.push_back("%Y-%m-%d %H:%M:%S"); @@ -287,19 +287,51 @@ TEST(chrono_test, system_clock_time_point) { EXPECT_EQ(sys_output, fmt::format(fmt::runtime(fmt_spec), tm)); } - if (std::find(spec_list.cbegin(), spec_list.cend(), "%z") != - spec_list.cend()) { + // Timezone formatters tests makes sense for localtime. +#if defined(__MINGW32__) && !defined(_UCRT) + spec_list = {"%Z"}; +#else + spec_list = {"%z", "%Z"}; +#endif + for (const auto& spec : spec_list) { + auto t = std::chrono::system_clock::to_time_t(t1); + auto tm = *std::localtime(&t); + + auto sys_output = system_strftime(spec, &tm); + + auto fmt_spec = fmt::format("{{:{}}}", spec); + EXPECT_EQ(sys_output, fmt::format(fmt::runtime(fmt_spec), tm)); + + if (spec == "%z") { + sys_output.insert(sys_output.end() - 2, 1, ':'); + EXPECT_EQ(sys_output, fmt::format("{:%Ez}", tm)); + EXPECT_EQ(sys_output, fmt::format("{:%Oz}", tm)); + } + } + + // Separate tests for UTC, since std::time_put can use local time and ignoring + // the timezone in std::tm (if it presents on platform). + if (fmt::detail::has_member_data_tm_zone::value) { auto t = std::chrono::system_clock::to_time_t(t1); auto tm = *std::gmtime(&t); - auto sys_output = system_strftime("%z", &tm); - sys_output.insert(sys_output.end() - 2, 1, ':'); + std::vector tz_names = {"GMT", "UTC"}; + EXPECT_THAT(tz_names, Contains(fmt::format("{:%Z}", t1))); + EXPECT_THAT(tz_names, Contains(fmt::format("{:%Z}", tm))); + } - EXPECT_EQ(sys_output, fmt::format("{:%Ez}", t1)); - EXPECT_EQ(sys_output, fmt::format("{:%Ez}", tm)); + if (fmt::detail::has_member_data_tm_gmtoff::value) { + auto t = std::chrono::system_clock::to_time_t(t1); + auto tm = *std::gmtime(&t); - EXPECT_EQ(sys_output, fmt::format("{:%Oz}", t1)); - EXPECT_EQ(sys_output, fmt::format("{:%Oz}", tm)); + EXPECT_EQ("+0000", fmt::format("{:%z}", t1)); + EXPECT_EQ("+0000", fmt::format("{:%z}", tm)); + + EXPECT_EQ("+00:00", fmt::format("{:%Ez}", t1)); + EXPECT_EQ("+00:00", fmt::format("{:%Ez}", tm)); + + EXPECT_EQ("+00:00", fmt::format("{:%Oz}", t1)); + EXPECT_EQ("+00:00", fmt::format("{:%Oz}", tm)); } } diff --git a/test/xchar-test.cc b/test/xchar-test.cc index 13cbcf2119a2..e859c64901d8 100644 --- a/test/xchar-test.cc +++ b/test/xchar-test.cc @@ -294,7 +294,7 @@ TEST(chrono_test_wchar, time_point) { L"%OU", L"%W", L"%OW", L"%V", L"%OV", L"%j", L"%d", L"%Od", L"%e", L"%Oe", L"%a", L"%A", L"%w", L"%Ow", L"%u", L"%Ou", L"%H", L"%OH", L"%I", L"%OI", L"%M", L"%OM", L"%S", L"%OS", L"%x", L"%Ex", L"%X", - L"%EX", L"%D", L"%F", L"%R", L"%T", L"%p", L"%z", L"%Z"}; + L"%EX", L"%D", L"%F", L"%R", L"%T", L"%p"}; #ifndef _WIN32 // Disabled on Windows, because these formats is not consistent among // platforms. @@ -303,7 +303,7 @@ TEST(chrono_test_wchar, time_point) { // Only C89 conversion specifiers when using MSVCRT instead of UCRT spec_list = {L"%%", L"%Y", L"%y", L"%b", L"%B", L"%m", L"%U", L"%W", L"%j", L"%d", L"%a", L"%A", L"%w", L"%H", - L"%I", L"%M", L"%S", L"%x", L"%X", L"%p", L"%Z"}; + L"%I", L"%M", L"%S", L"%x", L"%X", L"%p"}; #endif spec_list.push_back(L"%Y-%m-%d %H:%M:%S"); @@ -317,6 +317,53 @@ TEST(chrono_test_wchar, time_point) { EXPECT_EQ(sys_output, fmt::format(fmt::runtime(fmt_spec), t1)); EXPECT_EQ(sys_output, fmt::format(fmt::runtime(fmt_spec), tm)); } + + // Timezone formatters tests makes sense for localtime. +#if defined(__MINGW32__) && !defined(_UCRT) + spec_list = {L"%Z"}; +#else + spec_list = {L"%z", L"%Z"}; +#endif + for (const auto& spec : spec_list) { + auto t = std::chrono::system_clock::to_time_t(t1); + auto tm = *std::localtime(&t); + + auto sys_output = system_wcsftime(spec, &tm); + + auto fmt_spec = fmt::format(L"{{:{}}}", spec); + EXPECT_EQ(sys_output, fmt::format(fmt::runtime(fmt_spec), tm)); + + if (spec == L"%z") { + sys_output.insert(sys_output.end() - 2, 1, L':'); + EXPECT_EQ(sys_output, fmt::format(L"{:%Ez}", tm)); + EXPECT_EQ(sys_output, fmt::format(L"{:%Oz}", tm)); + } + } + + // Separate tests for UTC, since std::time_put can use local time and ignoring + // the timezone in std::tm (if it presents on platform). + if (fmt::detail::has_member_data_tm_zone::value) { + auto t = std::chrono::system_clock::to_time_t(t1); + auto tm = *std::gmtime(&t); + + std::vector tz_names = {L"GMT", L"UTC"}; + EXPECT_THAT(tz_names, Contains(fmt::format(L"{:%Z}", t1))); + EXPECT_THAT(tz_names, Contains(fmt::format(L"{:%Z}", tm))); + } + + if (fmt::detail::has_member_data_tm_gmtoff::value) { + auto t = std::chrono::system_clock::to_time_t(t1); + auto tm = *std::gmtime(&t); + + EXPECT_EQ(L"+0000", fmt::format(L"{:%z}", t1)); + EXPECT_EQ(L"+0000", fmt::format(L"{:%z}", tm)); + + EXPECT_EQ(L"+00:00", fmt::format(L"{:%Ez}", t1)); + EXPECT_EQ(L"+00:00", fmt::format(L"{:%Ez}", tm)); + + EXPECT_EQ(L"+00:00", fmt::format(L"{:%Oz}", t1)); + EXPECT_EQ(L"+00:00", fmt::format(L"{:%Oz}", tm)); + } } TEST(xchar_test, color) { From f8483726c4645882cd711bed4f7755193f518587 Mon Sep 17 00:00:00 2001 From: Vladislav Shchapov Date: Sun, 25 Dec 2022 22:24:07 +0500 Subject: [PATCH 3/3] Simplify C99 strftime detection conditions Signed-off-by: Vladislav Shchapov --- test/chrono-test.cc | 20 +++++++++++++------- test/xchar-test.cc | 15 +++++++++++---- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/test/chrono-test.cc b/test/chrono-test.cc index d4947005c057..9ac9142c5e4c 100644 --- a/test/chrono-test.cc +++ b/test/chrono-test.cc @@ -15,9 +15,15 @@ #include "util.h" // get_locale using fmt::runtime; - using testing::Contains; +#if defined(__MINGW32__) && !defined(_UCRT) +// Only C89 conversion specifiers when using MSVCRT instead of UCRT +# define FMT_HAS_C99_STRFTIME 0 +#else +# define FMT_HAS_C99_STRFTIME 1 +#endif + auto make_tm() -> std::tm { auto time = std::tm(); time.tm_mday = 1; @@ -123,7 +129,7 @@ TEST(chrono_test, format_tm) { make_tm(2000, 1, 3, 12, 14, 16) // W1 }; -#if defined(__MINGW32__) && !defined(_UCRT) +#if !FMT_HAS_C99_STRFTIME GTEST_SKIP() << "Skip the rest of this test because it relies on strftime() " "conforming to C99, but on this platform, MINGW + MSVCRT, " "the function conforms only to C89."; @@ -269,7 +275,7 @@ TEST(chrono_test, system_clock_time_point) { // Disabled on Windows because these formats are not consistent among // platforms. spec_list.insert(spec_list.end(), {"%c", "%Ec", "%r"}); -#elif defined(__MINGW32__) && !defined(_UCRT) +#elif !FMT_HAS_C99_STRFTIME // Only C89 conversion specifiers when using MSVCRT instead of UCRT spec_list = {"%%", "%Y", "%y", "%b", "%B", "%m", "%U", "%W", "%j", "%d", "%a", "%A", "%w", "%H", "%I", "%M", "%S", "%x", "%X", "%p"}; @@ -288,10 +294,10 @@ TEST(chrono_test, system_clock_time_point) { } // Timezone formatters tests makes sense for localtime. -#if defined(__MINGW32__) && !defined(_UCRT) - spec_list = {"%Z"}; -#else +#if FMT_HAS_C99_STRFTIME spec_list = {"%z", "%Z"}; +#else + spec_list = {"%Z"}; #endif for (const auto& spec : spec_list) { auto t = std::chrono::system_clock::to_time_t(t1); @@ -374,7 +380,7 @@ TEST(chrono_test, local_system_clock_time_point) { // Disabled on Windows because these formats are not consistent among // platforms. spec_list.insert(spec_list.end(), {"%c", "%Ec", "%r"}); -# elif defined(__MINGW32__) && !defined(_UCRT) +# elif !FMT_HAS_C99_STRFTIME // Only C89 conversion specifiers when using MSVCRT instead of UCRT spec_list = {"%%", "%Y", "%y", "%b", "%B", "%m", "%U", "%W", "%j", "%d", "%a", "%A", "%w", "%H", "%I", "%M", "%S", "%x", "%X", "%p", "%Z"}; diff --git a/test/xchar-test.cc b/test/xchar-test.cc index e859c64901d8..af26653f0226 100644 --- a/test/xchar-test.cc +++ b/test/xchar-test.cc @@ -22,6 +22,13 @@ using fmt::detail::max_value; using testing::Contains; +#if defined(__MINGW32__) && !defined(_UCRT) +// Only C89 conversion specifiers when using MSVCRT instead of UCRT +# define FMT_HAS_C99_STRFTIME 0 +#else +# define FMT_HAS_C99_STRFTIME 1 +#endif + namespace test_ns { template class test_string { private: @@ -299,7 +306,7 @@ TEST(chrono_test_wchar, time_point) { // Disabled on Windows, because these formats is not consistent among // platforms. spec_list.insert(spec_list.end(), {L"%c", L"%Ec", L"%r"}); -#elif defined(__MINGW32__) && !defined(_UCRT) +#elif !FMT_HAS_C99_STRFTIME // Only C89 conversion specifiers when using MSVCRT instead of UCRT spec_list = {L"%%", L"%Y", L"%y", L"%b", L"%B", L"%m", L"%U", L"%W", L"%j", L"%d", L"%a", L"%A", L"%w", L"%H", @@ -319,10 +326,10 @@ TEST(chrono_test_wchar, time_point) { } // Timezone formatters tests makes sense for localtime. -#if defined(__MINGW32__) && !defined(_UCRT) - spec_list = {L"%Z"}; -#else +#if FMT_HAS_C99_STRFTIME spec_list = {L"%z", L"%Z"}; +#else + spec_list = {L"%Z"}; #endif for (const auto& spec : spec_list) { auto t = std::chrono::system_clock::to_time_t(t1);