From f694ec3aceab8e2385deaf143ffe3c7e42cd30a2 Mon Sep 17 00:00:00 2001 From: ReturnKartikey Date: Thu, 8 Oct 2026 01:08:20 +0530 Subject: [PATCH] Fix subsecond formatting for pre-epoch local_time (#4975) --- include/fmt/chrono.h | 9 +++++++++ test/chrono-test.cc | 8 ++++++++ 2 files changed, 17 insertions(+) diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index 19571164..a1c13483 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -2245,6 +2245,15 @@ struct formatter, Char> } auto subsecs = detail::duration_cast(time_since_epoch - seconds_since_epoch); + if (subsecs.count() < 0) { + auto second = detail::duration_cast(std::chrono::seconds(1)); + if (t.tm_sec != 0) { + --t.tm_sec; + } else { + t = gmtime(seconds_since_epoch.count() - 1); + } + subsecs += second; + } return formatter::do_format(t, ctx, &subsecs); } }; diff --git a/test/chrono-test.cc b/test/chrono-test.cc index 818ec42f..d9ea18a1 100644 --- a/test/chrono-test.cc +++ b/test/chrono-test.cc @@ -332,6 +332,14 @@ TEST(chrono_test, local_time) { fmt::format_error, "no timezone"); EXPECT_THROW_MSG((void)fmt::format(fmt::runtime("{:%Z}"), time), fmt::format_error, "no timezone"); + + auto epoch = fmt::local_time(); + auto d = std::chrono::milliseconds(250); + EXPECT_EQ(fmt::format("{:%S}", epoch - d), "59.750"); + + auto pre_epoch = fmt::local_time( + std::chrono::milliseconds(-14398877)); + EXPECT_EQ(fmt::format("{:%S}", pre_epoch), "01.123"); } template ::value)>