diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index 64ebdabe..b957ab6f 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -2115,9 +2115,7 @@ template struct formatter, Char> : formatter { FMT_CONSTEXPR formatter() { - basic_string_view default_specs = - detail::string_literal{}; - this->do_parse(default_specs.begin(), default_specs.end()); + this->format_str = detail::string_literal{}; } template @@ -2145,9 +2143,7 @@ template struct formatter, Char> : formatter { FMT_CONSTEXPR formatter() { - basic_string_view default_specs = - detail::string_literal{}; - this->do_parse(default_specs.begin(), default_specs.end()); + this->format_str = detail::string_literal{}; } template @@ -2190,51 +2186,51 @@ struct formatter, template struct formatter { private: - enum class spec { - unknown, - year_month_day, - hh_mm_ss, - }; - spec spec_ = spec::unknown; - basic_string_view specs; + format_specs specs; + detail::arg_ref width_ref; protected: - template FMT_CONSTEXPR auto do_parse(It begin, It end) -> It { - if (begin != end && *begin == ':') ++begin; + basic_string_view format_str; + + FMT_CONSTEXPR auto do_parse(basic_format_parse_context& ctx) + -> decltype(ctx.begin()) { + auto begin = ctx.begin(), end = ctx.end(); + if (begin == end || *begin == '}') return end; + + begin = detail::parse_align(begin, end, specs); + if (begin == end) return end; + + begin = detail::parse_dynamic_spec(begin, end, specs.width, width_ref, ctx); + if (begin == end) return end; + end = detail::parse_chrono_format(begin, end, detail::tm_format_checker()); - // Replace default spec only if the new spec is not empty. - if (end != begin) specs = {begin, detail::to_unsigned(end - begin)}; + // Replace default format_str only if the new spec is not empty. + if (end != begin) format_str = {begin, detail::to_unsigned(end - begin)}; return end; } template auto do_format(const std::tm& tm, FormatContext& ctx, const Duration* subsecs) const -> decltype(ctx.out()) { + auto specs_copy = specs; + basic_memory_buffer buf; + auto out = std::back_inserter(buf); + detail::handle_dynamic_spec(specs_copy.width, + width_ref, ctx); + const auto loc_ref = ctx.locale(); detail::get_locale loc(static_cast(loc_ref), loc_ref); - auto w = detail::tm_writer( - loc, ctx.out(), tm, subsecs); - if (spec_ == spec::year_month_day) - w.on_iso_date(); - else if (spec_ == spec::hh_mm_ss) - w.on_iso_time(); - else - detail::parse_chrono_format(specs.begin(), specs.end(), w); - return w.out(); + auto w = + detail::tm_writer(loc, out, tm, subsecs); + detail::parse_chrono_format(format_str.begin(), format_str.end(), w); + return detail::write( + ctx.out(), basic_string_view(buf.data(), buf.size()), specs_copy); } public: FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) -> decltype(ctx.begin()) { - auto end = this->do_parse(ctx.begin(), ctx.end()); - // basic_string_view<>::compare isn't constexpr before C++17. - if (specs.size() == 2 && specs[0] == Char('%')) { - if (specs[1] == Char('F')) - spec_ = spec::year_month_day; - else if (specs[1] == Char('T')) - spec_ = spec::hh_mm_ss; - } - return end; + return this->do_parse(ctx); } template diff --git a/test/chrono-test.cc b/test/chrono-test.cc index 9ac9142c..65e99a9c 100644 --- a/test/chrono-test.cc +++ b/test/chrono-test.cc @@ -462,7 +462,7 @@ TEST(chrono_test, format_default) { fmt::format("{}", std::chrono::duration>(42))); } -TEST(chrono_test, align) { +TEST(chrono_test, duration_align) { auto s = std::chrono::seconds(42); EXPECT_EQ("42s ", fmt::format("{:5}", s)); EXPECT_EQ("42s ", fmt::format("{:{}}", s, 5)); @@ -478,6 +478,35 @@ TEST(chrono_test, align) { fmt::format("{:{}%H:%M:%S}", std::chrono::seconds(12345), 12)); } +TEST(chrono_test, tm_align) { + auto t = make_tm(1975, 12, 29, 12, 14, 16); + EXPECT_EQ("1975-12-29 12:14:16", fmt::format("{:%F %T}", t)); + EXPECT_EQ("1975-12-29 12:14:16 ", fmt::format("{:30%F %T}", t)); + EXPECT_EQ("1975-12-29 12:14:16 ", fmt::format("{:{}%F %T}", t, 30)); + EXPECT_EQ("1975-12-29 12:14:16 ", fmt::format("{:<30%F %T}", t)); + EXPECT_EQ(" 1975-12-29 12:14:16 ", fmt::format("{:^30%F %T}", t)); + EXPECT_EQ(" 1975-12-29 12:14:16", fmt::format("{:>30%F %T}", t)); + + EXPECT_EQ("1975-12-29 12:14:16***********", fmt::format("{:*<30%F %T}", t)); + EXPECT_EQ("*****1975-12-29 12:14:16******", fmt::format("{:*^30%F %T}", t)); + EXPECT_EQ("***********1975-12-29 12:14:16", fmt::format("{:*>30%F %T}", t)); +} + +TEST(chrono_test, tp_align) { + auto tp = std::chrono::time_point_cast( + std::chrono::system_clock::from_time_t(0)); + EXPECT_EQ("00:00.000000", fmt::format("{:%M:%S}", tp)); + EXPECT_EQ("00:00.000000 ", fmt::format("{:15%M:%S}", tp)); + EXPECT_EQ("00:00.000000 ", fmt::format("{:{}%M:%S}", tp, 15)); + EXPECT_EQ("00:00.000000 ", fmt::format("{:<15%M:%S}", tp)); + EXPECT_EQ(" 00:00.000000 ", fmt::format("{:^15%M:%S}", tp)); + EXPECT_EQ(" 00:00.000000", fmt::format("{:>15%M:%S}", tp)); + + EXPECT_EQ("00:00.000000***", fmt::format("{:*<15%M:%S}", tp)); + EXPECT_EQ("*00:00.000000**", fmt::format("{:*^15%M:%S}", tp)); + EXPECT_EQ("***00:00.000000", fmt::format("{:*>15%M:%S}", tp)); +} + TEST(chrono_test, format_specs) { EXPECT_EQ("%", fmt::format("{:%%}", std::chrono::seconds(0))); EXPECT_EQ("\n", fmt::format("{:%n}", std::chrono::seconds(0)));