diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index 2a78a8b3..7e04e5d0 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -405,18 +405,34 @@ inline size_t strftime(wchar_t* str, size_t count, const wchar_t* format, FMT_END_DETAIL_NAMESPACE -template -struct formatter, +template ::min() < std::chrono::duration::zero()>> +constexpr std::chrono::duration abs(std::chrono::duration d) +{ + return d >= d.zero() ? d : -d; +} + +template +struct formatter>, Char> : formatter { template auto format(std::chrono::time_point val, FormatContext& ctx) -> decltype(ctx.out()) { std::tm time = localtime(val); auto epoch = val.time_since_epoch(); - auto seconds = std::chrono::duration_cast(epoch).count() / 1000; - auto today = std::chrono::high_resolution_clock::duration(std::chrono::milliseconds(seconds * 1000)); - auto subseconds = std::chrono::duration_cast(epoch - today); - return formatter::format(time, ctx, subseconds.count()); + auto seconds = std::chrono::duration_cast(epoch); + auto subseconds = abs(std::chrono::duration_cast>(epoch - seconds)); + + if (subseconds.count() > 0) { + auto width = std::to_string(Period::den).size() - 1; + std::basic_stringstream os; + os.fill('0'); + os.width(width); + os << subseconds.count(); + auto formatted_ss = os.str(); + return formatter::format(time, ctx, formatted_ss); + } + return formatter::format(time, ctx); } }; @@ -432,7 +448,7 @@ template struct formatter { } template - auto format(const std::tm& tm, FormatContext& ctx, int subseconds = 0) const + auto format(const std::tm& tm, FormatContext& ctx, const std::basic_string subseconds = {}) const -> decltype(ctx.out()) { basic_memory_buffer tm_format; tm_format.append(specs.begin(), specs.end()); @@ -441,8 +457,9 @@ template struct formatter { // https://github.com/fmtlib/fmt/issues/2238 auto hasS = std::find(tm_format.begin(), tm_format.end(), 'S') != tm_format.end(); auto hasT = std::find(tm_format.begin(), tm_format.end(), 'T') != tm_format.end(); - auto writeSubseconds = (hasS || hasT) && (subseconds != 0); + auto writeSubseconds = (hasS || hasT) && (subseconds.size() > 0); if (writeSubseconds) + // should be std::use_facet>(locale).decimal_point(); tm_format.push_back('.'); else tm_format.push_back(' '); @@ -460,12 +477,14 @@ template struct formatter { buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH)); } + auto buf_end = buf.end() - 1; if (writeSubseconds) { - buf.append(std::to_string(subseconds)); + buf.append(subseconds); + buf_end = buf.end(); } // Remove the extra space. - return std::copy(buf.begin(), buf.end() - 1, ctx.out()); + return std::copy(buf.begin(), buf_end, ctx.out()); } basic_string_view specs; diff --git a/test/chrono-test.cc b/test/chrono-test.cc index 5f0b845b..5343de10 100644 --- a/test/chrono-test.cc +++ b/test/chrono-test.cc @@ -100,6 +100,10 @@ TEST(chrono_test, time_point) { std::chrono::time_point; auto t2 = time_point(std::chrono::seconds(42)); EXPECT_EQ(strftime(t2), fmt::format("{:%Y-%m-%d %H:%M:%S}", t2)); + using time_point_2 = std::chrono::time_point; + auto t3 = time_point_2(std::chrono::milliseconds(1023)); + EXPECT_EQ("01.023", fmt::format("{:%S}", t3)); } #ifndef FMT_STATIC_THOUSANDS_SEPARATOR