Refactor chrono formatting

This commit is contained in:
Victor Zverovich 2021-05-25 06:40:21 -07:00
parent b955e7a6b2
commit 35a2c2a743
2 changed files with 35 additions and 30 deletions

View File

@ -288,42 +288,46 @@ inline null<> localtime_s(...) { return null<>(); }
inline null<> gmtime_r(...) { return null<>(); }
inline null<> gmtime_s(...) { return null<>(); }
template <typename OutputIt>
OutputIt write(OutputIt out, const std::tm& time, const std::locale& loc,
char format, char modifier = 0) {
inline auto do_write(const std::tm& time, const std::locale& loc, char format,
char modifier) -> std::string {
auto&& os = std::ostringstream();
using iterator = std::ostreambuf_iterator<char>;
const auto& facet = std::use_facet<std::time_put<char, iterator>>(loc);
auto end = facet.put(os, os, ' ', &time, format, modifier);
if (end.failed()) FMT_THROW(format_error("failed to format time"));
auto str = os.str();
if (detail::is_utf8() && loc != std::locale::classic()) {
// char16_t and char32_t codecvts are broken in MSVC (linkage errors).
using code_unit = conditional_t<FMT_MSC_VER, wchar_t, char16_t>;
auto& f =
std::use_facet<std::codecvt<code_unit, char, std::mbstate_t>>(loc);
auto mb = std::mbstate_t();
const char* from_next = nullptr;
code_unit* to_next = nullptr;
constexpr size_t buf_size = 32;
code_unit buf[buf_size] = {};
auto result = f.in(mb, str.data(), str.data() + str.size(), from_next, buf,
buf + buf_size, to_next);
if (result != std::codecvt_base::ok)
if (!detail::is_utf8() || loc == std::locale::classic()) return str;
// char16_t and char32_t codecvts are broken in MSVC (linkage errors).
using code_unit = conditional_t<FMT_MSC_VER, wchar_t, char16_t>;
auto& f = std::use_facet<std::codecvt<code_unit, char, std::mbstate_t>>(loc);
auto mb = std::mbstate_t();
const char* from_next = nullptr;
code_unit* to_next = nullptr;
constexpr size_t buf_size = 32;
code_unit buf[buf_size] = {};
auto result = f.in(mb, str.data(), str.data() + str.size(), from_next, buf,
buf + buf_size, to_next);
if (result != std::codecvt_base::ok)
FMT_THROW(format_error("failed to format time"));
str.clear();
for (code_unit* p = buf; p != to_next; ++p) {
auto c = *p;
if (c < 0x80) {
str.push_back(static_cast<char>(c));
} else if (c < 0x800) {
str.push_back(static_cast<char>(0xc0 | (c >> 6)));
str.push_back(static_cast<char>(0x80 | (c & 0x3f)));
} else {
FMT_THROW(format_error("failed to format time"));
str.clear();
for (code_unit* p = buf; p != to_next; ++p) {
code_unit c = *p;
if (c < 0x80) {
str.push_back(static_cast<char>(c));
} else if (c < 0x800) {
str.push_back(static_cast<char>(0xc0 | (c >> 6)));
str.push_back(static_cast<char>(0x80 | (c & 0x3f)));
} else {
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);
}
} // namespace detail

View File

@ -376,13 +376,14 @@ TEST(chrono_test, unsigned_duration) {
EXPECT_EQ("42s", fmt::format("{}", std::chrono::duration<unsigned>(42)));
}
TEST(chrono_test, format_weekday) {
TEST(chrono_test, weekday) {
auto loc = get_locale("ru_RU.UTF-8");
std::locale::global(loc);
EXPECT_EQ(fmt::format("{}", fmt::weekday(1)), "Mon");
auto mon = fmt::weekday(1);
EXPECT_EQ(fmt::format("{}", mon), "Mon");
if (loc != std::locale::classic()) {
EXPECT_THAT((std::vector<std::string>{"пн", "Пн"}),
Contains(fmt::format(loc, "{:L}", fmt::weekday(1))));
Contains(fmt::format(loc, "{:L}", mon)));
}
}