From 622fe9910798f34dd2de657cb43615c021c1f7a8 Mon Sep 17 00:00:00 2001 From: zivshek Date: Wed, 27 Mar 2024 20:10:30 -0400 Subject: [PATCH] Implemented fmt::day, fmt::month, fmt::year and related unit tests (#3906) --- include/fmt/chrono.h | 105 +++++++++++++++++++++++++++++++++++++++++-- test/chrono-test.cc | 14 +++++- 2 files changed, 114 insertions(+), 5 deletions(-) diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index cff7d68b9db7..0b746f321346 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -2036,17 +2036,52 @@ struct chrono_formatter { #if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907 using weekday = std::chrono::weekday; +using day = std::chrono::day; +using month = std::chrono::month; +using year = std::chrono::year; #else // A fallback version of weekday. class weekday { private: - unsigned char value; + unsigned char value_; public: weekday() = default; - explicit constexpr weekday(unsigned wd) noexcept - : value(static_cast(wd != 7 ? wd : 0)) {} - constexpr auto c_encoding() const noexcept -> unsigned { return value; } + constexpr explicit weekday(unsigned wd) noexcept + : value_(static_cast(wd != 7 ? wd : 0)) {} + constexpr auto c_encoding() const noexcept -> unsigned { return value_; } +}; + +class day { + private: + unsigned char value_; + + public: + day() = default; + constexpr explicit day(unsigned d) noexcept + : value_(static_cast(d)) {} + constexpr explicit operator unsigned() const noexcept { return value_; } +}; + +class month { + private: + unsigned char value_; + + public: + month() = default; + constexpr explicit month(unsigned m) noexcept + : value_(static_cast(m)) {} + constexpr explicit operator unsigned() const noexcept { return value_; } +}; + +class year { + private: + int value_; + + public: + year() = default; + constexpr explicit year(int y) noexcept : value_(y) {} + constexpr explicit operator int() const noexcept { return value_; } }; class year_month_day {}; @@ -2079,6 +2114,68 @@ template struct formatter { } }; +template struct formatter { + FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) + -> decltype(ctx.begin()) { + return ctx.begin(); + } + + template + auto format(day d, FormatContext& ctx) const -> decltype(ctx.out()) { + auto time = std::tm(); + time.tm_mday = static_cast(static_cast(d)); + detail::get_locale loc(false, ctx.locale()); + auto w = detail::tm_writer(loc, ctx.out(), time); + w.on_day_of_month(detail::numeric_system::standard); + return w.out(); + } +}; + +template struct formatter { + private: + bool localized = false; + + public: + FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) + -> decltype(ctx.begin()) { + auto begin = ctx.begin(), end = ctx.end(); + if (begin != end && *begin == 'L') { + ++begin; + localized = true; + } + return begin; + } + + template + auto format(month m, FormatContext& ctx) const -> decltype(ctx.out()) { + auto time = std::tm(); + // std::chrono::month has a range of 1-12, std::tm requires 0-11 + time.tm_mon = static_cast(static_cast(m)) - 1; + detail::get_locale loc(localized, ctx.locale()); + auto w = detail::tm_writer(loc, ctx.out(), time); + w.on_abbr_month(); + return w.out(); + } +}; + +template struct formatter { + FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) + -> decltype(ctx.begin()) { + return ctx.begin(); + } + + template + auto format(year y, FormatContext& ctx) const -> decltype(ctx.out()) { + auto time = std::tm(); + // std::tm::tm_year is years since 1900 + time.tm_year = static_cast(y) - 1900; + detail::get_locale loc(true, ctx.locale()); + auto w = detail::tm_writer(loc, ctx.out(), time); + w.on_year(detail::numeric_system::standard); + return w.out(); + } +}; + template struct formatter, Char> { private: diff --git a/test/chrono-test.cc b/test/chrono-test.cc index fa237f22a586..d692e6c7b19a 100644 --- a/test/chrono-test.cc +++ b/test/chrono-test.cc @@ -1011,4 +1011,16 @@ TEST(chrono_test, glibc_extensions) { TEST(chrono_test, out_of_range) { auto d = std::chrono::duration(538976288); EXPECT_THROW((void)fmt::format("{:%j}", d), fmt::format_error); -} \ No newline at end of file +} + +TEST(chrono_test, year_month_day) { + auto loc = get_locale("es_ES.UTF-8"); + std::locale::global(loc); + auto year = fmt::year(2024); + auto month = fmt::month(1); + auto day = fmt::day(1); + + EXPECT_EQ(fmt::format("{}", year), "2024"); + EXPECT_EQ(fmt::format("{}", month), "Jan"); + EXPECT_EQ(fmt::format("{}", day), "01"); +}