From 369296cdc30202bae7cab25e4f983dcd9594e3e5 Mon Sep 17 00:00:00 2001 From: zivshek Date: Wed, 20 Dec 2023 23:55:14 -0500 Subject: [PATCH 01/15] added custom formatters for std::chrono::day/month/year --- include/fmt/chrono.h | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index 1a4b7d5ad08e..0e8280962b71 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -2042,6 +2042,33 @@ template struct formatter { } }; +template struct formatter { + FMT_CONSTEXPR auto parse(format_parse_context& ctx) { return ctx.begin(); } + + template + auto format(const std::chrono::day& value, FormatContext& ctx) { + return fmt::format_to(ctx.out(), "{}", static_cast(value)); + } +}; + +template struct formatter { + FMT_CONSTEXPR auto parse(format_parse_context& ctx) { return ctx.begin(); } + + template + auto format(const std::chrono::month& value, FormatContext& ctx) { + return fmt::format_to(ctx.out(), "{}", static_cast(value)); + } +}; + +template struct formatter { + FMT_CONSTEXPR auto parse(format_parse_context& ctx) { return ctx.begin(); } + + template + auto format(const std::chrono::year& value, FormatContext& ctx) { + return fmt::format_to(ctx.out(), "{}", static_cast(value)); + } +}; + template struct formatter, Char> { private: From 79d801a082bb1549bc4936b130b7c1d343a907a2 Mon Sep 17 00:00:00 2001 From: zivshek Date: Mon, 25 Mar 2024 14:59:04 -0400 Subject: [PATCH 02/15] Implemented fmt::day, fmt::month, fmt::year --- include/fmt/chrono.h | 88 ++++++++++++++++++++++++++++++++------------ include/fmt/core.h | 1 + test/chrono-test.cc | 23 ++++++++++++ test/color-test.cc | 11 ++++++ 4 files changed, 100 insertions(+), 23 deletions(-) diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index 0e8280962b71..261750b0747d 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -1999,18 +1999,27 @@ 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; - - 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; } -}; +// A fallback version. +#define DECLARE_CALENDAR_ELEMENT(CalendarElement, DataType, Init) \ + class CalendarElement { \ + private: \ + DataType value; \ + \ + public: \ + CalendarElement() = default; \ + explicit constexpr CalendarElement(DataType v) noexcept \ + : value(Init) {} \ + constexpr auto c_encoding() const noexcept -> DataType { return value; } \ + } \ + +DECLARE_CALENDAR_ELEMENT(weekday, unsigned char, v != 7 ? v : 0); +DECLARE_CALENDAR_ELEMENT(day, unsigned char, v); +DECLARE_CALENDAR_ELEMENT(month, unsigned char, v); +DECLARE_CALENDAR_ELEMENT(year, int, v - 1900); class year_month_day {}; #endif @@ -2042,30 +2051,63 @@ template struct formatter { } }; -template struct formatter { - FMT_CONSTEXPR auto parse(format_parse_context& ctx) { return ctx.begin(); } +template struct formatter { + FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) + -> decltype(ctx.begin()) { + return ctx.begin(); + } template - auto format(const std::chrono::day& value, FormatContext& ctx) { - return fmt::format_to(ctx.out(), "{}", static_cast(value)); + auto format(day d, FormatContext& ctx) const -> decltype(ctx.out()) { + auto time = std::tm(); + time.tm_mday = static_cast(d.c_encoding()); + 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 { - FMT_CONSTEXPR auto parse(format_parse_context& ctx) { return ctx.begin(); } +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(const std::chrono::month& value, FormatContext& ctx) { - return fmt::format_to(ctx.out(), "{}", static_cast(value)); + auto format(month m, FormatContext& ctx) const -> decltype(ctx.out()) { + auto time = std::tm(); + time.tm_mon = static_cast(m.c_encoding()); + 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(format_parse_context& ctx) { return ctx.begin(); } +template struct formatter { + FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) + -> decltype(ctx.begin()) { + return ctx.begin(); + } template - auto format(const std::chrono::year& value, FormatContext& ctx) { - return fmt::format_to(ctx.out(), "{}", static_cast(value)); + auto format(year y, FormatContext& ctx) const -> decltype(ctx.out()) { + auto time = std::tm(); + time.tm_year = y.c_encoding(); + 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(); } }; diff --git a/include/fmt/core.h b/include/fmt/core.h index ce9f0e70a002..b33609a69fd7 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -16,6 +16,7 @@ #include // std::addressof #include #include +#include // The fmt library version in the form major * 10000 + minor * 100 + patch. #define FMT_VERSION 100102 diff --git a/test/chrono-test.cc b/test/chrono-test.cc index 56fbd38216a8..82ab00990713 100644 --- a/test/chrono-test.cc +++ b/test/chrono-test.cc @@ -26,6 +26,9 @@ using testing::Contains; #if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907L using days = std::chrono::days; +using day = std::chrono::day; +using month = std::chrono::month; +using year = std::chrono::year; #else using days = std::chrono::duration>; #endif @@ -1027,4 +1030,24 @@ 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); +} + +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(0); + auto day = fmt::day(1); + + EXPECT_EQ(fmt::format("{}", year), "2024"); + EXPECT_EQ(fmt::format("{}", month), "Jan"); + EXPECT_EQ(fmt::format("{}", day), "01"); + + auto tm = std::tm(); + tm.tm_mday = static_cast(day.c_encoding()); + tm.tm_mon = static_cast(month.c_encoding()); + tm.tm_year = year.c_encoding(); + + EXPECT_EQ(fmt::format("{:%Y-%m-%d}", tm), "2024-01-01"); + EXPECT_EQ(fmt::format("{:%Y-%b-%d}", tm), "2024-Jan-01"); } \ No newline at end of file diff --git a/test/color-test.cc b/test/color-test.cc index c2ba13a977db..f4af870c7b2d 100644 --- a/test/color-test.cc +++ b/test/color-test.cc @@ -11,6 +11,9 @@ #include "gtest-extra.h" // EXPECT_WRITE +#include +#include "fmt/chrono.h" + TEST(color_test, format) { EXPECT_EQ(fmt::format(fg(fmt::rgb(255, 20, 30)), "rgb(255,20,30)"), "\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m"); @@ -70,3 +73,11 @@ TEST(color_test, print) { EXPECT_WRITE(stdout, fmt::print(fg(fmt::rgb(255, 20, 30)), "rgb(255,20,30)"), "\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m"); } + +TEST(color_test, greenday) { + EXPECT_EXIT( + { + fmt::print(fg(fmt::color::red), "{}", std::chrono::day(5)); + }, + testing::ExitedWithCode(0), ""); +} \ No newline at end of file From bd855f678eb8120f55096ca1e3aab42e4d826c6f Mon Sep 17 00:00:00 2001 From: zivshek Date: Mon, 25 Mar 2024 15:07:22 -0400 Subject: [PATCH 03/15] revert a test file that got committed accidentally --- test/color-test.cc | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/test/color-test.cc b/test/color-test.cc index f4af870c7b2d..c2ba13a977db 100644 --- a/test/color-test.cc +++ b/test/color-test.cc @@ -11,9 +11,6 @@ #include "gtest-extra.h" // EXPECT_WRITE -#include -#include "fmt/chrono.h" - TEST(color_test, format) { EXPECT_EQ(fmt::format(fg(fmt::rgb(255, 20, 30)), "rgb(255,20,30)"), "\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m"); @@ -73,11 +70,3 @@ TEST(color_test, print) { EXPECT_WRITE(stdout, fmt::print(fg(fmt::rgb(255, 20, 30)), "rgb(255,20,30)"), "\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m"); } - -TEST(color_test, greenday) { - EXPECT_EXIT( - { - fmt::print(fg(fmt::color::red), "{}", std::chrono::day(5)); - }, - testing::ExitedWithCode(0), ""); -} \ No newline at end of file From 96888007c3338314a089a6b80b15453be2c65f13 Mon Sep 17 00:00:00 2001 From: zivshek Date: Wed, 20 Dec 2023 23:55:14 -0500 Subject: [PATCH 04/15] added custom formatters for std::chrono::day/month/year --- include/fmt/chrono.h | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index cff7d68b9db7..3dab4ed949de 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -2079,6 +2079,33 @@ template struct formatter { } }; +template struct formatter { + FMT_CONSTEXPR auto parse(format_parse_context& ctx) { return ctx.begin(); } + + template + auto format(const std::chrono::day& value, FormatContext& ctx) { + return fmt::format_to(ctx.out(), "{}", static_cast(value)); + } +}; + +template struct formatter { + FMT_CONSTEXPR auto parse(format_parse_context& ctx) { return ctx.begin(); } + + template + auto format(const std::chrono::month& value, FormatContext& ctx) { + return fmt::format_to(ctx.out(), "{}", static_cast(value)); + } +}; + +template struct formatter { + FMT_CONSTEXPR auto parse(format_parse_context& ctx) { return ctx.begin(); } + + template + auto format(const std::chrono::year& value, FormatContext& ctx) { + return fmt::format_to(ctx.out(), "{}", static_cast(value)); + } +}; + template struct formatter, Char> { private: From 537270157f190eaa062d7b5cb5b26ee858f6bddd Mon Sep 17 00:00:00 2001 From: zivshek Date: Mon, 25 Mar 2024 14:59:04 -0400 Subject: [PATCH 05/15] Implemented fmt::day, fmt::month, fmt::year --- include/fmt/chrono.h | 88 ++++++++++++++++++++++++++++++++------------ include/fmt/format.h | 1 + test/chrono-test.cc | 23 ++++++++++++ test/color-test.cc | 11 ++++++ 4 files changed, 100 insertions(+), 23 deletions(-) diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index 3dab4ed949de..16d4bb89eacd 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -2036,18 +2036,27 @@ 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; - - 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; } -}; +// A fallback version. +#define DECLARE_CALENDAR_ELEMENT(CalendarElement, DataType, Init) \ + class CalendarElement { \ + private: \ + DataType value; \ + \ + public: \ + CalendarElement() = default; \ + explicit constexpr CalendarElement(DataType v) noexcept \ + : value(Init) {} \ + constexpr auto c_encoding() const noexcept -> DataType { return value; } \ + } \ + +DECLARE_CALENDAR_ELEMENT(weekday, unsigned char, v != 7 ? v : 0); +DECLARE_CALENDAR_ELEMENT(day, unsigned char, v); +DECLARE_CALENDAR_ELEMENT(month, unsigned char, v); +DECLARE_CALENDAR_ELEMENT(year, int, v - 1900); class year_month_day {}; #endif @@ -2079,30 +2088,63 @@ template struct formatter { } }; -template struct formatter { - FMT_CONSTEXPR auto parse(format_parse_context& ctx) { return ctx.begin(); } +template struct formatter { + FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) + -> decltype(ctx.begin()) { + return ctx.begin(); + } template - auto format(const std::chrono::day& value, FormatContext& ctx) { - return fmt::format_to(ctx.out(), "{}", static_cast(value)); + auto format(day d, FormatContext& ctx) const -> decltype(ctx.out()) { + auto time = std::tm(); + time.tm_mday = static_cast(d.c_encoding()); + 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 { - FMT_CONSTEXPR auto parse(format_parse_context& ctx) { return ctx.begin(); } +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(const std::chrono::month& value, FormatContext& ctx) { - return fmt::format_to(ctx.out(), "{}", static_cast(value)); + auto format(month m, FormatContext& ctx) const -> decltype(ctx.out()) { + auto time = std::tm(); + time.tm_mon = static_cast(m.c_encoding()); + 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(format_parse_context& ctx) { return ctx.begin(); } +template struct formatter { + FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) + -> decltype(ctx.begin()) { + return ctx.begin(); + } template - auto format(const std::chrono::year& value, FormatContext& ctx) { - return fmt::format_to(ctx.out(), "{}", static_cast(value)); + auto format(year y, FormatContext& ctx) const -> decltype(ctx.out()) { + auto time = std::tm(); + time.tm_year = y.c_encoding(); + 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(); } }; diff --git a/include/fmt/format.h b/include/fmt/format.h index 3aaee42f6708..b07514544bd4 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -52,6 +52,7 @@ #include // std::system_error #include "base.h" +#include // Checking FMT_CPLUSPLUS for warning suppression in MSVC. #if FMT_HAS_INCLUDE() && FMT_CPLUSPLUS > 201703L diff --git a/test/chrono-test.cc b/test/chrono-test.cc index fa237f22a586..3a3c5063cd2e 100644 --- a/test/chrono-test.cc +++ b/test/chrono-test.cc @@ -29,6 +29,9 @@ using sys_time = std::chrono::time_point; #if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907L using days = std::chrono::days; +using day = std::chrono::day; +using month = std::chrono::month; +using year = std::chrono::year; #else using days = std::chrono::duration>; #endif @@ -1011,4 +1014,24 @@ 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); +} + +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(0); + auto day = fmt::day(1); + + EXPECT_EQ(fmt::format("{}", year), "2024"); + EXPECT_EQ(fmt::format("{}", month), "Jan"); + EXPECT_EQ(fmt::format("{}", day), "01"); + + auto tm = std::tm(); + tm.tm_mday = static_cast(day.c_encoding()); + tm.tm_mon = static_cast(month.c_encoding()); + tm.tm_year = year.c_encoding(); + + EXPECT_EQ(fmt::format("{:%Y-%m-%d}", tm), "2024-01-01"); + EXPECT_EQ(fmt::format("{:%Y-%b-%d}", tm), "2024-Jan-01"); } \ No newline at end of file diff --git a/test/color-test.cc b/test/color-test.cc index c2ba13a977db..f4af870c7b2d 100644 --- a/test/color-test.cc +++ b/test/color-test.cc @@ -11,6 +11,9 @@ #include "gtest-extra.h" // EXPECT_WRITE +#include +#include "fmt/chrono.h" + TEST(color_test, format) { EXPECT_EQ(fmt::format(fg(fmt::rgb(255, 20, 30)), "rgb(255,20,30)"), "\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m"); @@ -70,3 +73,11 @@ TEST(color_test, print) { EXPECT_WRITE(stdout, fmt::print(fg(fmt::rgb(255, 20, 30)), "rgb(255,20,30)"), "\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m"); } + +TEST(color_test, greenday) { + EXPECT_EXIT( + { + fmt::print(fg(fmt::color::red), "{}", std::chrono::day(5)); + }, + testing::ExitedWithCode(0), ""); +} \ No newline at end of file From ced6deb0f474264b8e81fc50c51df66a3fa41955 Mon Sep 17 00:00:00 2001 From: zivshek Date: Mon, 25 Mar 2024 15:07:22 -0400 Subject: [PATCH 06/15] revert a test file that got committed accidentally --- test/color-test.cc | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/test/color-test.cc b/test/color-test.cc index f4af870c7b2d..c2ba13a977db 100644 --- a/test/color-test.cc +++ b/test/color-test.cc @@ -11,9 +11,6 @@ #include "gtest-extra.h" // EXPECT_WRITE -#include -#include "fmt/chrono.h" - TEST(color_test, format) { EXPECT_EQ(fmt::format(fg(fmt::rgb(255, 20, 30)), "rgb(255,20,30)"), "\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m"); @@ -73,11 +70,3 @@ TEST(color_test, print) { EXPECT_WRITE(stdout, fmt::print(fg(fmt::rgb(255, 20, 30)), "rgb(255,20,30)"), "\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m"); } - -TEST(color_test, greenday) { - EXPECT_EXIT( - { - fmt::print(fg(fmt::color::red), "{}", std::chrono::day(5)); - }, - testing::ExitedWithCode(0), ""); -} \ No newline at end of file From 01c6abac746c4937570ba3ccefbb6d1e2e12712f Mon Sep 17 00:00:00 2001 From: zivshek Date: Mon, 25 Mar 2024 15:24:29 -0400 Subject: [PATCH 07/15] remove unnecessary using statements in chrono-test --- test/chrono-test.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/chrono-test.cc b/test/chrono-test.cc index 3a3c5063cd2e..837a7176d832 100644 --- a/test/chrono-test.cc +++ b/test/chrono-test.cc @@ -29,9 +29,6 @@ using sys_time = std::chrono::time_point; #if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907L using days = std::chrono::days; -using day = std::chrono::day; -using month = std::chrono::month; -using year = std::chrono::year; #else using days = std::chrono::duration>; #endif From 18a7bf78eb7f3c770facb1c605860a70ba2c27c3 Mon Sep 17 00:00:00 2001 From: zivshek Date: Mon, 25 Mar 2024 17:08:09 -0400 Subject: [PATCH 08/15] Fixed a compiler error in CPP20 --- include/fmt/chrono.h | 65 ++++++++++++++++++++++++++++++-------------- test/chrono-test.cc | 8 +++--- 2 files changed, 49 insertions(+), 24 deletions(-) diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index 16d4bb89eacd..6af09c5f84a6 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -2040,23 +2040,48 @@ using day = std::chrono::day; using month = std::chrono::month; using year = std::chrono::year; #else -// A fallback version. -#define DECLARE_CALENDAR_ELEMENT(CalendarElement, DataType, Init) \ - class CalendarElement { \ - private: \ - DataType value; \ - \ - public: \ - CalendarElement() = default; \ - explicit constexpr CalendarElement(DataType v) noexcept \ - : value(Init) {} \ - constexpr auto c_encoding() const noexcept -> DataType { return value; } \ - } \ - -DECLARE_CALENDAR_ELEMENT(weekday, unsigned char, v != 7 ? v : 0); -DECLARE_CALENDAR_ELEMENT(day, unsigned char, v); -DECLARE_CALENDAR_ELEMENT(month, unsigned char, v); -DECLARE_CALENDAR_ELEMENT(year, int, v - 1900); +// A fallback version of weekday. +class weekday { + private: + unsigned char value; + + public: + weekday() = default; + explicit constexpr weekday(unsigned char wd) noexcept + : value(wd != 7 ? wd : 0) {} + constexpr auto c_encoding() const noexcept -> unsigned char { return value; } +}; + +class day { + private: + unsigned char value; + + public: + day() = default; + explicit constexpr day(unsigned char d) noexcept + : value(d) {} + constexpr explicit operator unsigned int () const noexcept { return value; } +}; + +class month { + private: + unsigned char value; + + public: + month() = default; + explicit constexpr month(unsigned char m) noexcept : value(m - 1) {} + constexpr explicit operator unsigned int() const noexcept { return value; } +}; + +class year { + private: + int value; + + public: + year() = default; + explicit constexpr year(int y) noexcept : value(y) {} + constexpr explicit operator int() const noexcept { return value; } +}; class year_month_day {}; #endif @@ -2097,7 +2122,7 @@ template struct formatter { template auto format(day d, FormatContext& ctx) const -> decltype(ctx.out()) { auto time = std::tm(); - time.tm_mday = static_cast(d.c_encoding()); + 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); @@ -2123,7 +2148,7 @@ template struct formatter { template auto format(month m, FormatContext& ctx) const -> decltype(ctx.out()) { auto time = std::tm(); - time.tm_mon = static_cast(m.c_encoding()); + time.tm_mon = static_cast(static_cast(m)); detail::get_locale loc(localized, ctx.locale()); auto w = detail::tm_writer(loc, ctx.out(), time); w.on_abbr_month(); @@ -2140,7 +2165,7 @@ template struct formatter { template auto format(year y, FormatContext& ctx) const -> decltype(ctx.out()) { auto time = std::tm(); - time.tm_year = y.c_encoding(); + time.tm_year = static_cast(y); detail::get_locale loc(true, ctx.locale()); auto w = detail::tm_writer(loc, ctx.out(), time); w.on_year(detail::numeric_system::standard); diff --git a/test/chrono-test.cc b/test/chrono-test.cc index 837a7176d832..37f382a0b654 100644 --- a/test/chrono-test.cc +++ b/test/chrono-test.cc @@ -1016,7 +1016,7 @@ TEST(chrono_test, out_of_range) { TEST(chrono_test, year_month_day) { auto loc = get_locale("es_ES.UTF-8"); std::locale::global(loc); - auto year = fmt::year(2024); + auto year = fmt::year(2024 - 1900); auto month = fmt::month(0); auto day = fmt::day(1); @@ -1025,9 +1025,9 @@ TEST(chrono_test, year_month_day) { EXPECT_EQ(fmt::format("{}", day), "01"); auto tm = std::tm(); - tm.tm_mday = static_cast(day.c_encoding()); - tm.tm_mon = static_cast(month.c_encoding()); - tm.tm_year = year.c_encoding(); + tm.tm_mday = static_cast(static_cast(day)); + tm.tm_mon = static_cast(static_cast(month)); + tm.tm_year = static_cast(year); EXPECT_EQ(fmt::format("{:%Y-%m-%d}", tm), "2024-01-01"); EXPECT_EQ(fmt::format("{:%Y-%b-%d}", tm), "2024-Jan-01"); From 32ff11efbc2d73645fbc42fbf8ab2a45f15dbac8 Mon Sep 17 00:00:00 2001 From: zivshek Date: Mon, 25 Mar 2024 17:17:09 -0400 Subject: [PATCH 09/15] take uint instead unsigned char for fmt::day and fmt::month as in std::chrono --- include/fmt/chrono.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index 6af09c5f84a6..849f95ce3dfb 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -2047,8 +2047,8 @@ class weekday { public: weekday() = default; - explicit constexpr weekday(unsigned char wd) noexcept - : value(wd != 7 ? wd : 0) {} + explicit constexpr weekday(unsigned int wd) noexcept + : value(static_cast(wd != 7 ? wd : 0)) {} constexpr auto c_encoding() const noexcept -> unsigned char { return value; } }; @@ -2058,8 +2058,8 @@ class day { public: day() = default; - explicit constexpr day(unsigned char d) noexcept - : value(d) {} + explicit constexpr day(unsigned int d) noexcept + : value(static_cast(d)) {} constexpr explicit operator unsigned int () const noexcept { return value; } }; @@ -2069,7 +2069,8 @@ class month { public: month() = default; - explicit constexpr month(unsigned char m) noexcept : value(m - 1) {} + explicit constexpr month(unsigned int m) noexcept + : value(static_cast(m)) {} constexpr explicit operator unsigned int() const noexcept { return value; } }; From 5c46fb1e9f9cb2d511c2f185f9898ec390cd1631 Mon Sep 17 00:00:00 2001 From: zivshek Date: Mon, 25 Mar 2024 17:19:37 -0400 Subject: [PATCH 10/15] fix weekday::c_encoding return value --- include/fmt/chrono.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index 849f95ce3dfb..60cb6ef986a1 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -2049,7 +2049,7 @@ class weekday { weekday() = default; explicit constexpr weekday(unsigned int wd) noexcept : value(static_cast(wd != 7 ? wd : 0)) {} - constexpr auto c_encoding() const noexcept -> unsigned char { return value; } + constexpr auto c_encoding() const noexcept -> unsigned int { return value; } }; class day { From 9c9f697b3137b4ee3d4e8afdb5f04ee03dfafc96 Mon Sep 17 00:00:00 2001 From: zivshek Date: Mon, 25 Mar 2024 20:52:10 -0400 Subject: [PATCH 11/15] fixed an issue that std uses a range of 1-12 for month, but fmt uses 0-11 --- include/fmt/chrono.h | 3 ++- test/chrono-test.cc | 10 +--------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index 60cb6ef986a1..011086c402ed 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -2149,7 +2149,8 @@ template struct formatter { template auto format(month m, FormatContext& ctx) const -> decltype(ctx.out()) { auto time = std::tm(); - time.tm_mon = static_cast(static_cast(m)); + // std::chrono::month has a range of 1-12, fmt 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(); diff --git a/test/chrono-test.cc b/test/chrono-test.cc index 37f382a0b654..9494f439572d 100644 --- a/test/chrono-test.cc +++ b/test/chrono-test.cc @@ -1017,18 +1017,10 @@ TEST(chrono_test, year_month_day) { auto loc = get_locale("es_ES.UTF-8"); std::locale::global(loc); auto year = fmt::year(2024 - 1900); - auto month = fmt::month(0); + 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"); - - auto tm = std::tm(); - tm.tm_mday = static_cast(static_cast(day)); - tm.tm_mon = static_cast(static_cast(month)); - tm.tm_year = static_cast(year); - - EXPECT_EQ(fmt::format("{:%Y-%m-%d}", tm), "2024-01-01"); - EXPECT_EQ(fmt::format("{:%Y-%b-%d}", tm), "2024-Jan-01"); } \ No newline at end of file From f0e1693490df5c133db0a9c23bf76ffa0638c7d9 Mon Sep 17 00:00:00 2001 From: zivshek Date: Mon, 25 Mar 2024 15:07:22 -0400 Subject: [PATCH 12/15] added custom formatters for std::chrono::day/month/year Implemented fmt::day, fmt::month, fmt::year revert a test file that got committed accidentally remove unnecessary using statements in chrono-test Fixed a compiler error in CPP20 take uint instead unsigned char for fmt::day and fmt::month as in std::chrono fix weekday::c_encoding return value fixed an issue that std uses a range of 1-12 for month, but fmt uses 0-11 --- include/fmt/chrono.h | 67 +++++++++++++++++++++++++++++++------------- test/chrono-test.cc | 15 ++-------- test/color-test.cc | 11 -------- 3 files changed, 49 insertions(+), 44 deletions(-) diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index 16d4bb89eacd..011086c402ed 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -2040,23 +2040,49 @@ using day = std::chrono::day; using month = std::chrono::month; using year = std::chrono::year; #else -// A fallback version. -#define DECLARE_CALENDAR_ELEMENT(CalendarElement, DataType, Init) \ - class CalendarElement { \ - private: \ - DataType value; \ - \ - public: \ - CalendarElement() = default; \ - explicit constexpr CalendarElement(DataType v) noexcept \ - : value(Init) {} \ - constexpr auto c_encoding() const noexcept -> DataType { return value; } \ - } \ - -DECLARE_CALENDAR_ELEMENT(weekday, unsigned char, v != 7 ? v : 0); -DECLARE_CALENDAR_ELEMENT(day, unsigned char, v); -DECLARE_CALENDAR_ELEMENT(month, unsigned char, v); -DECLARE_CALENDAR_ELEMENT(year, int, v - 1900); +// A fallback version of weekday. +class weekday { + private: + unsigned char value; + + public: + weekday() = default; + explicit constexpr weekday(unsigned int wd) noexcept + : value(static_cast(wd != 7 ? wd : 0)) {} + constexpr auto c_encoding() const noexcept -> unsigned int { return value; } +}; + +class day { + private: + unsigned char value; + + public: + day() = default; + explicit constexpr day(unsigned int d) noexcept + : value(static_cast(d)) {} + constexpr explicit operator unsigned int () const noexcept { return value; } +}; + +class month { + private: + unsigned char value; + + public: + month() = default; + explicit constexpr month(unsigned int m) noexcept + : value(static_cast(m)) {} + constexpr explicit operator unsigned int() const noexcept { return value; } +}; + +class year { + private: + int value; + + public: + year() = default; + explicit constexpr year(int y) noexcept : value(y) {} + constexpr explicit operator int() const noexcept { return value; } +}; class year_month_day {}; #endif @@ -2097,7 +2123,7 @@ template struct formatter { template auto format(day d, FormatContext& ctx) const -> decltype(ctx.out()) { auto time = std::tm(); - time.tm_mday = static_cast(d.c_encoding()); + 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); @@ -2123,7 +2149,8 @@ template struct formatter { template auto format(month m, FormatContext& ctx) const -> decltype(ctx.out()) { auto time = std::tm(); - time.tm_mon = static_cast(m.c_encoding()); + // std::chrono::month has a range of 1-12, fmt 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(); @@ -2140,7 +2167,7 @@ template struct formatter { template auto format(year y, FormatContext& ctx) const -> decltype(ctx.out()) { auto time = std::tm(); - time.tm_year = y.c_encoding(); + time.tm_year = static_cast(y); detail::get_locale loc(true, ctx.locale()); auto w = detail::tm_writer(loc, ctx.out(), time); w.on_year(detail::numeric_system::standard); diff --git a/test/chrono-test.cc b/test/chrono-test.cc index 3a3c5063cd2e..9494f439572d 100644 --- a/test/chrono-test.cc +++ b/test/chrono-test.cc @@ -29,9 +29,6 @@ using sys_time = std::chrono::time_point; #if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907L using days = std::chrono::days; -using day = std::chrono::day; -using month = std::chrono::month; -using year = std::chrono::year; #else using days = std::chrono::duration>; #endif @@ -1019,19 +1016,11 @@ TEST(chrono_test, out_of_range) { 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(0); + auto year = fmt::year(2024 - 1900); + 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"); - - auto tm = std::tm(); - tm.tm_mday = static_cast(day.c_encoding()); - tm.tm_mon = static_cast(month.c_encoding()); - tm.tm_year = year.c_encoding(); - - EXPECT_EQ(fmt::format("{:%Y-%m-%d}", tm), "2024-01-01"); - EXPECT_EQ(fmt::format("{:%Y-%b-%d}", tm), "2024-Jan-01"); } \ No newline at end of file diff --git a/test/color-test.cc b/test/color-test.cc index f4af870c7b2d..c2ba13a977db 100644 --- a/test/color-test.cc +++ b/test/color-test.cc @@ -11,9 +11,6 @@ #include "gtest-extra.h" // EXPECT_WRITE -#include -#include "fmt/chrono.h" - TEST(color_test, format) { EXPECT_EQ(fmt::format(fg(fmt::rgb(255, 20, 30)), "rgb(255,20,30)"), "\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m"); @@ -73,11 +70,3 @@ TEST(color_test, print) { EXPECT_WRITE(stdout, fmt::print(fg(fmt::rgb(255, 20, 30)), "rgb(255,20,30)"), "\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m"); } - -TEST(color_test, greenday) { - EXPECT_EXIT( - { - fmt::print(fg(fmt::color::red), "{}", std::chrono::day(5)); - }, - testing::ExitedWithCode(0), ""); -} \ No newline at end of file From 032edf1d33471e96a1a9670c762341f37e7ef33b Mon Sep 17 00:00:00 2001 From: zivshek Date: Tue, 26 Mar 2024 10:02:10 -0400 Subject: [PATCH 13/15] address some feedback from code review --- include/fmt/chrono.h | 39 ++++++++++++++++++++------------------- test/chrono-test.cc | 2 +- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index 011086c402ed..0b746f321346 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -2043,45 +2043,45 @@ using year = std::chrono::year; // A fallback version of weekday. class weekday { private: - unsigned char value; + unsigned char value_; public: weekday() = default; - explicit constexpr weekday(unsigned int wd) noexcept - : value(static_cast(wd != 7 ? wd : 0)) {} - constexpr auto c_encoding() const noexcept -> unsigned int { 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; + unsigned char value_; public: day() = default; - explicit constexpr day(unsigned int d) noexcept - : value(static_cast(d)) {} - constexpr explicit operator unsigned int () const noexcept { return value; } + constexpr explicit day(unsigned d) noexcept + : value_(static_cast(d)) {} + constexpr explicit operator unsigned() const noexcept { return value_; } }; class month { private: - unsigned char value; + unsigned char value_; public: month() = default; - explicit constexpr month(unsigned int m) noexcept - : value(static_cast(m)) {} - constexpr explicit operator unsigned int() const noexcept { return value; } + constexpr explicit month(unsigned m) noexcept + : value_(static_cast(m)) {} + constexpr explicit operator unsigned() const noexcept { return value_; } }; class year { private: - int value; + int value_; public: year() = default; - explicit constexpr year(int y) noexcept : value(y) {} - constexpr explicit operator int() const noexcept { return value; } + constexpr explicit year(int y) noexcept : value_(y) {} + constexpr explicit operator int() const noexcept { return value_; } }; class year_month_day {}; @@ -2123,7 +2123,7 @@ template struct formatter { template auto format(day d, FormatContext& ctx) const -> decltype(ctx.out()) { auto time = std::tm(); - time.tm_mday = static_cast(static_cast(d)); + 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); @@ -2149,8 +2149,8 @@ template struct formatter { template auto format(month m, FormatContext& ctx) const -> decltype(ctx.out()) { auto time = std::tm(); - // std::chrono::month has a range of 1-12, fmt requires 0-11 - time.tm_mon = static_cast(static_cast(m)) - 1; + // 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(); @@ -2167,7 +2167,8 @@ template struct formatter { template auto format(year y, FormatContext& ctx) const -> decltype(ctx.out()) { auto time = std::tm(); - time.tm_year = static_cast(y); + // 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); diff --git a/test/chrono-test.cc b/test/chrono-test.cc index 9494f439572d..24854ca16a85 100644 --- a/test/chrono-test.cc +++ b/test/chrono-test.cc @@ -1016,7 +1016,7 @@ TEST(chrono_test, out_of_range) { TEST(chrono_test, year_month_day) { auto loc = get_locale("es_ES.UTF-8"); std::locale::global(loc); - auto year = fmt::year(2024 - 1900); + auto year = fmt::year(2024); auto month = fmt::month(1); auto day = fmt::day(1); From d7d5087379349917288f45327b2ce34c2beba988 Mon Sep 17 00:00:00 2001 From: zivshek Date: Tue, 26 Mar 2024 10:03:59 -0400 Subject: [PATCH 14/15] adding newline to the test file --- test/chrono-test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/chrono-test.cc b/test/chrono-test.cc index 24854ca16a85..d692e6c7b19a 100644 --- a/test/chrono-test.cc +++ b/test/chrono-test.cc @@ -1023,4 +1023,4 @@ TEST(chrono_test, year_month_day) { EXPECT_EQ(fmt::format("{}", year), "2024"); EXPECT_EQ(fmt::format("{}", month), "Jan"); EXPECT_EQ(fmt::format("{}", day), "01"); -} \ No newline at end of file +} From e2e3f84959b411f0d8fba8a3a4be8c7aea87cd93 Mon Sep 17 00:00:00 2001 From: zivshek Date: Tue, 26 Mar 2024 10:12:48 -0400 Subject: [PATCH 15/15] remove unnecessary include (that I added) in format.h --- include/fmt/format.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/fmt/format.h b/include/fmt/format.h index b07514544bd4..3aaee42f6708 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -52,7 +52,6 @@ #include // std::system_error #include "base.h" -#include // Checking FMT_CPLUSPLUS for warning suppression in MSVC. #if FMT_HAS_INCLUDE() && FMT_CPLUSPLUS > 201703L