Skip to content

Commit

Permalink
Support fill, align & width for time point (#3260)
Browse files Browse the repository at this point in the history
  • Loading branch information
ShawnZhong committed Jan 9, 2023
1 parent 2622cd2 commit dda5308
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 36 deletions.
66 changes: 31 additions & 35 deletions include/fmt/chrono.h
Original file line number Diff line number Diff line change
Expand Up @@ -2115,9 +2115,7 @@ template <typename Char, typename Duration>
struct formatter<std::chrono::time_point<std::chrono::system_clock, Duration>,
Char> : formatter<std::tm, Char> {
FMT_CONSTEXPR formatter() {
basic_string_view<Char> default_specs =
detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>{};
this->do_parse(default_specs.begin(), default_specs.end());
this->format_str = detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>{};
}

template <typename FormatContext>
Expand Down Expand Up @@ -2145,9 +2143,7 @@ template <typename Char, typename Duration>
struct formatter<std::chrono::local_time<Duration>, Char>
: formatter<std::tm, Char> {
FMT_CONSTEXPR formatter() {
basic_string_view<Char> default_specs =
detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>{};
this->do_parse(default_specs.begin(), default_specs.end());
this->format_str = detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>{};
}

template <typename FormatContext>
Expand Down Expand Up @@ -2190,51 +2186,51 @@ struct formatter<std::chrono::time_point<std::chrono::utc_clock, Duration>,

template <typename Char> struct formatter<std::tm, Char> {
private:
enum class spec {
unknown,
year_month_day,
hh_mm_ss,
};
spec spec_ = spec::unknown;
basic_string_view<Char> specs;
format_specs<Char> specs;
detail::arg_ref<Char> width_ref;

protected:
template <typename It> FMT_CONSTEXPR auto do_parse(It begin, It end) -> It {
if (begin != end && *begin == ':') ++begin;
basic_string_view<Char> format_str;

FMT_CONSTEXPR auto do_parse(basic_format_parse_context<Char>& 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 <typename FormatContext, typename Duration>
auto do_format(const std::tm& tm, FormatContext& ctx,
const Duration* subsecs) const -> decltype(ctx.out()) {
auto specs_copy = specs;
basic_memory_buffer<Char> buf;
auto out = std::back_inserter(buf);
detail::handle_dynamic_spec<detail::width_checker>(specs_copy.width,
width_ref, ctx);

const auto loc_ref = ctx.locale();
detail::get_locale loc(static_cast<bool>(loc_ref), loc_ref);
auto w = detail::tm_writer<decltype(ctx.out()), Char, Duration>(
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<decltype(out), Char, Duration>(loc, out, tm, subsecs);
detail::parse_chrono_format(format_str.begin(), format_str.end(), w);
return detail::write(
ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs_copy);
}

public:
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& 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 <typename FormatContext>
Expand Down
31 changes: 30 additions & 1 deletion test/chrono-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,7 @@ TEST(chrono_test, format_default) {
fmt::format("{}", std::chrono::duration<int, std::ratio<15, 4>>(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));
Expand All @@ -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::microseconds>(
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)));
Expand Down

0 comments on commit dda5308

Please sign in to comment.