Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented fmt::day, fmt::month, fmt::year and related unit tests #3906

Merged
merged 17 commits into from
Mar 28, 2024
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 98 additions & 2 deletions include/fmt/chrono.h
Original file line number Diff line number Diff line change
Expand Up @@ -2036,6 +2036,9 @@ 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 {
Expand All @@ -2044,9 +2047,41 @@ class weekday {

public:
weekday() = default;
explicit constexpr weekday(unsigned wd) noexcept
explicit constexpr weekday(unsigned int wd) noexcept
zivshek marked this conversation as resolved.
Show resolved Hide resolved
: value(static_cast<unsigned char>(wd != 7 ? wd : 0)) {}
constexpr auto c_encoding() const noexcept -> unsigned { return value; }
constexpr auto c_encoding() const noexcept -> unsigned int { return value; }
};

class day {
private:
unsigned char value;
zivshek marked this conversation as resolved.
Show resolved Hide resolved

public:
day() = default;
explicit constexpr day(unsigned int d) noexcept
zivshek marked this conversation as resolved.
Show resolved Hide resolved
: value(static_cast<unsigned char>(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<unsigned char>(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 {};
Expand Down Expand Up @@ -2079,6 +2114,67 @@ template <typename Char> struct formatter<weekday, Char> {
}
};

template <typename Char> struct formatter<day, Char> {
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
return ctx.begin();
}

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 int>(d));
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);
return w.out();
}
};

template <typename Char> struct formatter<month, Char> {
private:
bool localized = 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;
}
return begin;
}

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, fmt requires 0-11
zivshek marked this conversation as resolved.
Show resolved Hide resolved
time.tm_mon = static_cast<int>(static_cast<unsigned int>(m)) - 1;
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> {
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
return ctx.begin();
}

template <typename FormatContext>
auto format(year y, FormatContext& ctx) const -> decltype(ctx.out()) {
auto time = std::tm();
time.tm_year = static_cast<int>(y);
detail::get_locale loc(true, 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 Rep, typename Period, typename Char>
struct formatter<std::chrono::duration<Rep, Period>, Char> {
private:
Expand Down
1 change: 1 addition & 0 deletions include/fmt/format.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
#include <system_error> // std::system_error

#include "base.h"
#include <chrono>

// Checking FMT_CPLUSPLUS for warning suppression in MSVC.
#if FMT_HAS_INCLUDE(<bit>) && FMT_CPLUSPLUS > 201703L
Expand Down
12 changes: 12 additions & 0 deletions test/chrono-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1011,4 +1011,16 @@ TEST(chrono_test, glibc_extensions) {
TEST(chrono_test, out_of_range) {
auto d = std::chrono::duration<unsigned long, std::giga>(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 - 1900);
zivshek marked this conversation as resolved.
Show resolved Hide resolved
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");
}
zivshek marked this conversation as resolved.
Show resolved Hide resolved