Add xchar support to chrono formatter

This commit is contained in:
Vladislav Shchapov 2021-10-28 19:36:57 +05:00
parent d70ec75b9d
commit 6b37e4e81a
2 changed files with 92 additions and 61 deletions

View File

@ -289,16 +289,32 @@ inline null<> localtime_s(...) { return null<>(); }
inline null<> gmtime_r(...) { return null<>(); } inline null<> gmtime_r(...) { return null<>(); }
inline null<> gmtime_s(...) { return null<>(); } inline null<> gmtime_s(...) { return null<>(); }
template <typename Char>
inline auto do_write(const std::tm& time, const std::locale& loc, char format, inline auto do_write(const std::tm& time, const std::locale& loc, char format,
char modifier) -> std::string { char modifier) -> std::basic_string<Char> {
auto&& os = std::ostringstream(); auto&& os = std::basic_ostringstream<Char>();
os.imbue(loc); os.imbue(loc);
using iterator = std::ostreambuf_iterator<char>; using iterator = std::ostreambuf_iterator<Char>;
const auto& facet = std::use_facet<std::time_put<char, iterator>>(loc); const auto& facet = std::use_facet<std::time_put<Char, iterator>>(loc);
auto end = facet.put(os, os, ' ', &time, format, modifier); auto end = facet.put(os, os, Char(' '), &time, format, modifier);
if (end.failed()) FMT_THROW(format_error("failed to format time")); if (end.failed()) FMT_THROW(format_error("failed to format time"));
auto str = os.str(); return std::move(os).str();
if (!detail::is_utf8() || loc == std::locale::classic()) return str; }
template <typename Char, typename OutputIt,
FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
auto write(OutputIt out, const std::tm& time, const std::locale& loc,
char format, char modifier = 0) -> OutputIt {
auto str = do_write<Char>(time, loc, format, modifier);
return std::copy(str.begin(), str.end(), out);
}
template <typename Char, typename OutputIt,
FMT_ENABLE_IF(std::is_same<Char, char>::value)>
auto write(OutputIt out, const std::tm& time, const std::locale& loc,
char format, char modifier = 0) -> OutputIt {
auto str = do_write<char>(time, loc, format, modifier);
if (detail::is_utf8() && loc != std::locale::classic()) {
// char16_t and char32_t codecvts are broken in MSVC (linkage errors) and // char16_t and char32_t codecvts are broken in MSVC (linkage errors) and
// gcc-4. // gcc-4.
#if FMT_MSC_VER != 0 || \ #if FMT_MSC_VER != 0 || \
@ -358,15 +374,10 @@ inline auto do_write(const std::tm& time, const std::locale& loc, char format,
FMT_THROW(format_error("failed to format time")); FMT_THROW(format_error("failed to format time"));
} }
} }
return str;
} }
template <typename OutputIt>
auto write(OutputIt out, const std::tm& time, const std::locale& loc,
char format, char modifier = 0) -> OutputIt {
auto str = do_write(time, loc, format, modifier);
return std::copy(str.begin(), str.end(), out); return std::copy(str.begin(), str.end(), out);
} }
} // namespace detail } // namespace detail
FMT_MODULE_EXPORT_BEGIN FMT_MODULE_EXPORT_BEGIN
@ -1067,7 +1078,7 @@ struct chrono_formatter {
if (isnan(val)) return write_nan(); if (isnan(val)) return write_nan();
const auto& loc = localized ? context.locale().template get<std::locale>() const auto& loc = localized ? context.locale().template get<std::locale>()
: std::locale::classic(); : std::locale::classic();
out = detail::write(out, time, loc, format, modifier); out = detail::write<char_type>(out, time, loc, format, modifier);
} }
void on_text(const char_type* begin, const char_type* end) { void on_text(const char_type* begin, const char_type* end) {
@ -1215,12 +1226,13 @@ class year_month_day {};
#endif #endif
// A rudimentary weekday formatter. // A rudimentary weekday formatter.
template <> struct formatter<weekday> { template <typename Char> struct formatter<weekday, Char> {
private: private:
bool localized = false; bool localized = false;
public: public:
FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
auto begin = ctx.begin(), end = ctx.end(); auto begin = ctx.begin(), end = ctx.end();
if (begin != end && *begin == 'L') { if (begin != end && *begin == 'L') {
++begin; ++begin;
@ -1229,12 +1241,13 @@ template <> struct formatter<weekday> {
return begin; return begin;
} }
auto format(weekday wd, format_context& ctx) -> decltype(ctx.out()) { template <typename FormatContext>
auto format(weekday wd, FormatContext& ctx) const -> decltype(ctx.out()) {
auto time = std::tm(); auto time = std::tm();
time.tm_wday = static_cast<int>(wd.c_encoding()); time.tm_wday = static_cast<int>(wd.c_encoding());
const auto& loc = localized ? ctx.locale().template get<std::locale>() const auto& loc = localized ? ctx.locale().template get<std::locale>()
: std::locale::classic(); : std::locale::classic();
return detail::write(ctx.out(), time, loc, 'a'); return detail::write<Char>(ctx.out(), time, loc, 'a');
} }
}; };

View File

@ -15,9 +15,11 @@
#include "fmt/color.h" #include "fmt/color.h"
#include "fmt/ostream.h" #include "fmt/ostream.h"
#include "fmt/ranges.h" #include "fmt/ranges.h"
#include "gtest/gtest.h" #include "gtest-extra.h" // Contains
#include "util.h" // get_locale
using fmt::detail::max_value; using fmt::detail::max_value;
using testing::Contains;
namespace test_ns { namespace test_ns {
template <typename Char> class test_string { template <typename Char> class test_string {
@ -466,4 +468,20 @@ TEST(locale_test, complex) {
EXPECT_EQ(fmt::format("{:8}", std::complex<double>(1, 2)), " (1+2i)"); EXPECT_EQ(fmt::format("{:8}", std::complex<double>(1, 2)), " (1+2i)");
} }
TEST(locale_test, chrono_weekday) {
auto loc = get_locale("ru_RU.UTF-8", "Russian_Russia.1251");
auto loc_old = std::locale::global(loc);
auto mon = fmt::weekday(1);
EXPECT_EQ(fmt::format(L"{}", mon), L"Mon");
if (loc != std::locale::classic()) {
// {L"\x43F\x43D", L"\x41F\x43D", L"\x43F\x43D\x434", L"\x41F\x43D\x434"}
// {L"пн", L"Пн", L"пнд", L"Пнд"}
EXPECT_THAT(
(std::vector<std::wstring>{L"\x43F\x43D", L"\x41F\x43D",
L"\x43F\x43D\x434", L"\x41F\x43D\x434"}),
Contains(fmt::format(loc, L"{:L}", mon)));
}
std::locale::global(loc_old);
}
#endif // FMT_STATIC_THOUSANDS_SEPARATOR #endif // FMT_STATIC_THOUSANDS_SEPARATOR