Skip to content

Commit 74a1872

Browse files
authored
Implemented fmt::day, fmt::month, fmt::year and related unit tests (#3906)
1 parent 88620e5 commit 74a1872

File tree

2 files changed

+114
-5
lines changed

2 files changed

+114
-5
lines changed

include/fmt/chrono.h

+101-4
Original file line numberDiff line numberDiff line change
@@ -2036,17 +2036,52 @@ struct chrono_formatter {
20362036

20372037
#if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907
20382038
using weekday = std::chrono::weekday;
2039+
using day = std::chrono::day;
2040+
using month = std::chrono::month;
2041+
using year = std::chrono::year;
20392042
#else
20402043
// A fallback version of weekday.
20412044
class weekday {
20422045
private:
2043-
unsigned char value;
2046+
unsigned char value_;
20442047

20452048
public:
20462049
weekday() = default;
2047-
explicit constexpr weekday(unsigned wd) noexcept
2048-
: value(static_cast<unsigned char>(wd != 7 ? wd : 0)) {}
2049-
constexpr auto c_encoding() const noexcept -> unsigned { return value; }
2050+
constexpr explicit weekday(unsigned wd) noexcept
2051+
: value_(static_cast<unsigned char>(wd != 7 ? wd : 0)) {}
2052+
constexpr auto c_encoding() const noexcept -> unsigned { return value_; }
2053+
};
2054+
2055+
class day {
2056+
private:
2057+
unsigned char value_;
2058+
2059+
public:
2060+
day() = default;
2061+
constexpr explicit day(unsigned d) noexcept
2062+
: value_(static_cast<unsigned char>(d)) {}
2063+
constexpr explicit operator unsigned() const noexcept { return value_; }
2064+
};
2065+
2066+
class month {
2067+
private:
2068+
unsigned char value_;
2069+
2070+
public:
2071+
month() = default;
2072+
constexpr explicit month(unsigned m) noexcept
2073+
: value_(static_cast<unsigned char>(m)) {}
2074+
constexpr explicit operator unsigned() const noexcept { return value_; }
2075+
};
2076+
2077+
class year {
2078+
private:
2079+
int value_;
2080+
2081+
public:
2082+
year() = default;
2083+
constexpr explicit year(int y) noexcept : value_(y) {}
2084+
constexpr explicit operator int() const noexcept { return value_; }
20502085
};
20512086

20522087
class year_month_day {};
@@ -2079,6 +2114,68 @@ template <typename Char> struct formatter<weekday, Char> {
20792114
}
20802115
};
20812116

2117+
template <typename Char> struct formatter<day, Char> {
2118+
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
2119+
-> decltype(ctx.begin()) {
2120+
return ctx.begin();
2121+
}
2122+
2123+
template <typename FormatContext>
2124+
auto format(day d, FormatContext& ctx) const -> decltype(ctx.out()) {
2125+
auto time = std::tm();
2126+
time.tm_mday = static_cast<int>(static_cast<unsigned>(d));
2127+
detail::get_locale loc(false, ctx.locale());
2128+
auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), time);
2129+
w.on_day_of_month(detail::numeric_system::standard);
2130+
return w.out();
2131+
}
2132+
};
2133+
2134+
template <typename Char> struct formatter<month, Char> {
2135+
private:
2136+
bool localized = false;
2137+
2138+
public:
2139+
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
2140+
-> decltype(ctx.begin()) {
2141+
auto begin = ctx.begin(), end = ctx.end();
2142+
if (begin != end && *begin == 'L') {
2143+
++begin;
2144+
localized = true;
2145+
}
2146+
return begin;
2147+
}
2148+
2149+
template <typename FormatContext>
2150+
auto format(month m, FormatContext& ctx) const -> decltype(ctx.out()) {
2151+
auto time = std::tm();
2152+
// std::chrono::month has a range of 1-12, std::tm requires 0-11
2153+
time.tm_mon = static_cast<int>(static_cast<unsigned>(m)) - 1;
2154+
detail::get_locale loc(localized, ctx.locale());
2155+
auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), time);
2156+
w.on_abbr_month();
2157+
return w.out();
2158+
}
2159+
};
2160+
2161+
template <typename Char> struct formatter<year, Char> {
2162+
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
2163+
-> decltype(ctx.begin()) {
2164+
return ctx.begin();
2165+
}
2166+
2167+
template <typename FormatContext>
2168+
auto format(year y, FormatContext& ctx) const -> decltype(ctx.out()) {
2169+
auto time = std::tm();
2170+
// std::tm::tm_year is years since 1900
2171+
time.tm_year = static_cast<int>(y) - 1900;
2172+
detail::get_locale loc(true, ctx.locale());
2173+
auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), time);
2174+
w.on_year(detail::numeric_system::standard);
2175+
return w.out();
2176+
}
2177+
};
2178+
20822179
template <typename Rep, typename Period, typename Char>
20832180
struct formatter<std::chrono::duration<Rep, Period>, Char> {
20842181
private:

test/chrono-test.cc

+13-1
Original file line numberDiff line numberDiff line change
@@ -1011,4 +1011,16 @@ TEST(chrono_test, glibc_extensions) {
10111011
TEST(chrono_test, out_of_range) {
10121012
auto d = std::chrono::duration<unsigned long, std::giga>(538976288);
10131013
EXPECT_THROW((void)fmt::format("{:%j}", d), fmt::format_error);
1014-
}
1014+
}
1015+
1016+
TEST(chrono_test, year_month_day) {
1017+
auto loc = get_locale("es_ES.UTF-8");
1018+
std::locale::global(loc);
1019+
auto year = fmt::year(2024);
1020+
auto month = fmt::month(1);
1021+
auto day = fmt::day(1);
1022+
1023+
EXPECT_EQ(fmt::format("{}", year), "2024");
1024+
EXPECT_EQ(fmt::format("{}", month), "Jan");
1025+
EXPECT_EQ(fmt::format("{}", day), "01");
1026+
}

0 commit comments

Comments
 (0)