diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 23e70830..42a2d2ce 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -58,7 +58,7 @@ jobs: - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 - name: Set timezone - run: sudo timedatectl set-timezone 'Europe/Minsk' + run: sudo timedatectl set-timezone 'Europe/Kyiv' - name: Install GCC 4.9 run: | diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index 50c777c8..7ed4ba21 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -2320,15 +2320,20 @@ struct formatter, Char> : formatter { template auto format(local_time val, FormatContext& ctx) const -> decltype(ctx.out()) { + auto time_since_epoch = val.time_since_epoch(); + auto seconds_since_epoch = + detail::duration_cast(time_since_epoch); + // Use gmtime to prevent time conversion since local_time has an + // unspecified time zone. + auto t = gmtime(seconds_since_epoch.count()); using period = typename Duration::period; if (period::num == 1 && period::den == 1 && !std::is_floating_point::value) { - return formatter::format(localtime(val), ctx); + return formatter::format(t, ctx); } - auto epoch = val.time_since_epoch(); - auto subsecs = detail::duration_cast( - epoch - detail::duration_cast(epoch)); - return formatter::do_format(localtime(val), ctx, &subsecs); + auto subsecs = + detail::duration_cast(time_since_epoch - seconds_since_epoch); + return formatter::do_format(t, ctx, &subsecs); } }; diff --git a/test/chrono-test.cc b/test/chrono-test.cc index 33eeb184..7d3032be 100644 --- a/test/chrono-test.cc +++ b/test/chrono-test.cc @@ -428,6 +428,18 @@ TEST(chrono_test, local_system_clock_time_point) { #endif // FMT_USE_LOCAL_TIME +TEST(chrono_test, daylight_savings_time_end) { + // 2024-10-27 03:05 as the number of seconds since epoch in Europe/Kyiv time. + // It is slightly after the DST end and passing it to to_sys will result in + // an ambiguous time error: + // 2024-10-27 03:05:00 is ambiguous. It could be + // 2024-10-27 03:05:00 EEST == 2024-10-27 00:05:00 UTC or + // 2024-10-27 03:05:00 EET == 2024-10-27 01:05:00 UTC + auto t = + fmt::local_time(std::chrono::seconds(1729998300)); + EXPECT_EQ(fmt::format("{}", t), "2024-10-27 03:05:00"); +} + #ifndef FMT_STATIC_THOUSANDS_SEPARATOR TEST(chrono_test, format_default) { @@ -1033,7 +1045,7 @@ TEST(chrono_test, glibc_extensions) { { auto t = std::tm(); t.tm_year = -5 - 1900; - EXPECT_EQ(fmt::format( "{:%Y}", t), "-005"); + EXPECT_EQ(fmt::format("{:%Y}", t), "-005"); EXPECT_EQ(fmt::format("{:%_Y}", t), " -5"); EXPECT_EQ(fmt::format("{:%-Y}", t), "-5"); } @@ -1045,8 +1057,6 @@ TEST(chrono_test, glibc_extensions) { EXPECT_EQ(fmt::format("{:%_m}", t), " 7"); EXPECT_EQ(fmt::format("{:%-m}", t), "7"); } - - } TEST(chrono_test, out_of_range) {