implement year_month_day (#3913)

* implement year_month_day, also changed weekday, day, month, year's formatter to use formatter<std::tm, Char> so they all support the format strings
* support ":L" for month and weekday
This commit is contained in:
zivshek 2024-04-03 18:35:53 -04:00 committed by GitHub
parent 843e293564
commit 71144eeaff
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 126 additions and 32 deletions

View File

@ -2040,6 +2040,7 @@ using weekday = std::chrono::weekday;
using day = std::chrono::day;
using month = std::chrono::month;
using year = std::chrono::year;
using year_month_day = std::chrono::year_month_day;
#else
// A fallback version of weekday.
class weekday {
@ -2085,46 +2086,75 @@ class year {
constexpr explicit operator int() const noexcept { return value_; }
};
class year_month_day {};
class year_month_day {
private:
fmt::year year_;
fmt::month month_;
fmt::day day_;
public:
year_month_day() = default;
constexpr year_month_day(const year& y, const month& m, const day& d) noexcept
: year_(y), month_(m), day_(d) {}
constexpr fmt::year year() const noexcept { return year_; }
constexpr fmt::month month() const noexcept { return month_; }
constexpr fmt::day day() const noexcept { return day_; }
};
#endif
// A rudimentary weekday formatter.
template <typename Char> struct formatter<weekday, Char> {
template <typename Char>
struct formatter<weekday, Char> : private formatter<std::tm, Char> {
private:
bool localized = false;
bool localized_{false};
bool use_tm_formatter_{false};
public:
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
auto begin = ctx.begin(), end = ctx.end();
if (begin != end && *begin == 'L') {
++begin;
localized = true;
auto it = ctx.begin(), end = ctx.end();
if (it != end && *it == 'L') {
++it;
localized_ = true;
return it;
}
return begin;
use_tm_formatter_ = it != end && *it != '}';
return use_tm_formatter_ ? formatter<std::tm, Char>::parse(ctx) : it;
}
template <typename FormatContext>
auto format(weekday wd, FormatContext& ctx) const -> decltype(ctx.out()) {
auto time = std::tm();
time.tm_wday = static_cast<int>(wd.c_encoding());
detail::get_locale loc(localized, ctx.locale());
if (use_tm_formatter_) {
return formatter<std::tm, Char>::format(time, ctx);
}
detail::get_locale loc(localized_, ctx.locale());
auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), time);
w.on_abbr_weekday();
return w.out();
}
};
template <typename Char> struct formatter<day, Char> {
template <typename Char>
struct formatter<day, Char> : private formatter<std::tm, Char> {
private:
bool use_tm_formatter_{false};
public:
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
return ctx.begin();
auto it = ctx.begin(), end = ctx.end();
use_tm_formatter_ = it != end && *it != '}';
return use_tm_formatter_ ? formatter<std::tm, Char>::parse(ctx) : it;
}
template <typename FormatContext>
auto format(day d, FormatContext& ctx) const -> decltype(ctx.out()) {
auto time = std::tm();
time.tm_mday = static_cast<int>(static_cast<unsigned>(d));
if (use_tm_formatter_) {
return formatter<std::tm, Char>::format(time, ctx);
}
detail::get_locale loc(false, ctx.locale());
auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), time);
w.on_day_of_month(detail::numeric_system::standard);
@ -2132,51 +2162,96 @@ template <typename Char> struct formatter<day, Char> {
}
};
template <typename Char> struct formatter<month, Char> {
template <typename Char>
struct formatter<month, Char> : private formatter<std::tm, Char> {
private:
bool localized = false;
bool localized_{false};
bool use_tm_formatter_{false};
public:
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
auto begin = ctx.begin(), end = ctx.end();
if (begin != end && *begin == 'L') {
++begin;
localized = true;
auto it = ctx.begin(), end = ctx.end();
if (it != end && *it == 'L') {
++it;
localized_ = true;
return it;
}
return begin;
use_tm_formatter_ = it != end && *it != '}';
return use_tm_formatter_ ? formatter<std::tm, Char>::parse(ctx) : it;
}
template <typename FormatContext>
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<int>(static_cast<unsigned>(m)) - 1;
detail::get_locale loc(localized, ctx.locale());
if (use_tm_formatter_) {
return formatter<std::tm, Char>::format(time, ctx);
}
detail::get_locale loc(localized_, ctx.locale());
auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), time);
w.on_abbr_month();
return w.out();
}
};
template <typename Char> struct formatter<year, Char> {
template <typename Char>
struct formatter<year, Char> : private formatter<std::tm, Char> {
private:
bool use_tm_formatter_{false};
public:
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
return ctx.begin();
auto it = ctx.begin(), end = ctx.end();
use_tm_formatter_ = it != end && *it != '}';
return use_tm_formatter_ ? formatter<std::tm, Char>::parse(ctx) : it;
}
template <typename FormatContext>
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<int>(y) - 1900;
detail::get_locale loc(true, ctx.locale());
if (use_tm_formatter_) {
return formatter<std::tm, Char>::format(time, ctx);
}
detail::get_locale loc(false, ctx.locale());
auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), time);
w.on_year(detail::numeric_system::standard);
return w.out();
}
};
template <typename Char>
struct formatter<year_month_day, Char> : private formatter<std::tm, Char> {
private:
bool use_tm_formatter_{false};
public:
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
auto it = ctx.begin(), end = ctx.end();
use_tm_formatter_ = it != end && *it != '}';
return use_tm_formatter_ ? formatter<std::tm, Char>::parse(ctx) : it;
}
template <typename FormatContext>
auto format(year_month_day val, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto time = std::tm();
time.tm_year = static_cast<int>(val.year()) - 1900;
time.tm_mon = static_cast<int>(static_cast<unsigned>(val.month())) - 1;
time.tm_mday = static_cast<int>(static_cast<unsigned>(val.day()));
if (use_tm_formatter_) {
return formatter<std::tm, Char>::format(time, ctx);
}
detail::get_locale loc(true, ctx.locale());
auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), time);
w.on_iso_date();
return w.out();
}
};
template <typename Rep, typename Period, typename Char>
struct formatter<std::chrono::duration<Rep, Period>, Char> {
private:

View File

@ -751,18 +751,17 @@ TEST(chrono_test, unsigned_duration) {
TEST(chrono_test, weekday) {
auto loc = get_locale("es_ES.UTF-8");
std::locale::global(loc);
auto sat = fmt::weekday(6);
auto tm = std::tm();
tm.tm_wday = static_cast<int>(sat.c_encoding());
EXPECT_EQ(fmt::format("{}", sat), "Sat");
EXPECT_EQ(fmt::format("{:%a}", tm), "Sat");
EXPECT_EQ(fmt::format("{:%a}", sat), "Sat");
EXPECT_EQ(fmt::format("{:%A}", sat), "Saturday");
if (loc != std::locale::classic()) {
auto saturdays = std::vector<std::string>{"sáb", "sá."};
EXPECT_THAT(saturdays, Contains(fmt::format(loc, "{:L}", sat)));
EXPECT_THAT(saturdays, Contains(fmt::format(loc, "{:%a}", tm)));
EXPECT_THAT(saturdays, Contains(fmt::format(loc, "{:%a}", sat)));
}
}
@ -1014,13 +1013,33 @@ 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(1);
auto day = fmt::day(1);
auto ymd = fmt::year_month_day(year, month, day);
EXPECT_EQ(fmt::format("{}", year), "2024");
EXPECT_EQ(fmt::format("{:%Y}", year), "2024");
EXPECT_EQ(fmt::format("{:%y}", year), "24");
EXPECT_EQ(fmt::format("{}", month), "Jan");
EXPECT_EQ(fmt::format("{:%m}", month), "01");
EXPECT_EQ(fmt::format("{:%b}", month), "Jan");
EXPECT_EQ(fmt::format("{:%B}", month), "January");
EXPECT_EQ(fmt::format("{}", day), "01");
EXPECT_EQ(fmt::format("{:%d}", day), "01");
EXPECT_EQ(fmt::format("{}", ymd), "2024-01-01");
EXPECT_EQ(fmt::format("{:%Y-%m-%d}", ymd), "2024-01-01");
EXPECT_EQ(fmt::format("{:%Y-%b-%d}", ymd), "2024-Jan-01");
EXPECT_EQ(fmt::format("{:%Y-%B-%d}", ymd), "2024-January-01");
auto loc = get_locale("es_ES.UTF-8");
std::locale::global(loc);
if (loc != std::locale::classic()) {
auto months = std::vector<std::string>{"ene.", "ene"};
EXPECT_THAT(months, Contains(fmt::format(loc, "{:L}", month)));
EXPECT_THAT(months, Contains(fmt::format(loc, "{:%b}", month)));
}
}