From 608a9a3441c374c41db9c5571c570e8faa3db0a1 Mon Sep 17 00:00:00 2001 From: Vladislav Shchapov Date: Sat, 27 Nov 2021 14:25:48 +0500 Subject: [PATCH] Use tm.tm_zone --- include/fmt/chrono.h | 197 +++++++++++++++++++++++++++---------------- 1 file changed, 124 insertions(+), 73 deletions(-) diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index 76b2330b..5825a810 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -291,6 +291,109 @@ inline null<> localtime_s(...) { return null<>(); } inline null<> gmtime_r(...) { return null<>(); } inline null<> gmtime_s(...) { return null<>(); } +inline const std::locale& get_classic_locale() { + static const auto& locale = std::locale::classic(); + return locale; +} + +template struct codecvt_result { + static constexpr const size_t max_size = 32; + CodeUnit buf[max_size]; + CodeUnit* end; +}; +template +constexpr const size_t codecvt_result::max_size; + +template +void write_codecvt(codecvt_result& out, string_view in_buf, + const std::locale& loc) { + using codecvt = std::codecvt; +#if FMT_CLANG_VERSION +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wdeprecated" + auto& f = std::use_facet(loc); +# pragma clang diagnostic pop +#else + auto& f = std::use_facet(loc); +#endif + auto mb = std::mbstate_t(); + const char* from_next = nullptr; + auto result = f.in(mb, in_buf.begin(), in_buf.end(), from_next, + std::begin(out.buf), std::end(out.buf), out.end); + if (result != std::codecvt_base::ok) + FMT_THROW(format_error("failed to format time")); +} + +template +auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc) + -> OutputIt { + if (detail::is_utf8() && loc != get_classic_locale()) { + // char16_t and char32_t codecvts are broken in MSVC (linkage errors) and + // gcc-4. +#if FMT_MSC_VER != 0 || \ + (defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI)) + // The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5 + // and newer. + using code_unit = wchar_t; +#else + using code_unit = char32_t; +#endif + + using unit_t = codecvt_result; + unit_t unit; + write_codecvt(unit, in, loc); + // In UTF-8 is used one to four one-byte code units. + auto&& buf = basic_memory_buffer(); + for (code_unit* p = unit.buf; p != unit.end; ++p) { + uint32_t c = static_cast(*p); + if (sizeof(code_unit) == 2 && c >= 0xd800 && c <= 0xdfff) { + // surrogate pair + ++p; + if (p == unit.end || (c & 0xfc00) != 0xd800 || + (*p & 0xfc00) != 0xdc00) { + FMT_THROW(format_error("failed to format time")); + } + c = (c << 10) + static_cast(*p) - 0x35fdc00; + } + if (c < 0x80) { + buf.push_back(static_cast(c)); + } else if (c < 0x800) { + buf.push_back(static_cast(0xc0 | (c >> 6))); + buf.push_back(static_cast(0x80 | (c & 0x3f))); + } else if ((c >= 0x800 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xffff)) { + buf.push_back(static_cast(0xe0 | (c >> 12))); + buf.push_back(static_cast(0x80 | ((c & 0xfff) >> 6))); + buf.push_back(static_cast(0x80 | (c & 0x3f))); + } else if (c >= 0x10000 && c <= 0x10ffff) { + buf.push_back(static_cast(0xf0 | (c >> 18))); + buf.push_back(static_cast(0x80 | ((c & 0x3ffff) >> 12))); + buf.push_back(static_cast(0x80 | ((c & 0xfff) >> 6))); + buf.push_back(static_cast(0x80 | (c & 0x3f))); + } else { + FMT_THROW(format_error("failed to format time")); + } + } + return copy_str(buf.data(), buf.data() + buf.size(), out); + } + return copy_str(in.data(), in.data() + in.size(), out); +} + +template ::value)> +auto write_tm_str(OutputIt out, string_view sv, const std::locale& loc) + -> OutputIt { + codecvt_result unit; + write_codecvt(unit, sv, loc); + return copy_str(unit.buf, unit.end, out); +} + +template ::value)> +auto write_tm_str(OutputIt out, string_view sv, const std::locale& loc) + -> OutputIt { + return write_encoded_tm_str(out, sv, loc); +} + template inline void do_write(buffer& buf, const std::tm& time, const std::locale& loc, char format, char modifier) { @@ -307,84 +410,18 @@ template ::value)> auto write(OutputIt out, const std::tm& time, const std::locale& loc, char format, char modifier = 0) -> OutputIt { - auto&& buffer = get_buffer(out); - do_write(buffer, time, loc, format, modifier); - return buffer.out(); -} - -inline const std::locale& get_classic_locale() { - static const auto& locale = std::locale::classic(); - return locale; + auto&& buf = get_buffer(out); + do_write(buf, time, loc, format, modifier); + return buf.out(); } template ::value)> auto write(OutputIt out, const std::tm& time, const std::locale& loc, char format, char modifier = 0) -> OutputIt { - auto&& buffer = basic_memory_buffer(); - do_write(buffer, time, loc, format, modifier); - if (detail::is_utf8() && loc != get_classic_locale()) { - // char16_t and char32_t codecvts are broken in MSVC (linkage errors) and - // gcc-4. -#if FMT_MSC_VER != 0 || \ - (defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI)) - // The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5 - // and newer. - using code_unit = wchar_t; -#else - using code_unit = char32_t; -#endif - - using codecvt = std::codecvt; -#if FMT_CLANG_VERSION -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wdeprecated" - auto& f = std::use_facet(loc); -# pragma clang diagnostic pop -#else - auto& f = std::use_facet(loc); -#endif - - 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, buffer.data(), buffer.data() + buffer.size(), - from_next, buf, buf + buf_size, to_next); - if (result != std::codecvt_base::ok) - FMT_THROW(format_error("failed to format time")); - buffer.clear(); - for (code_unit* p = buf; p != to_next; ++p) { - uint32_t c = static_cast(*p); - if (sizeof(code_unit) == 2 && c >= 0xd800 && c <= 0xdfff) { - // surrogate pair - ++p; - if (p == to_next || (c & 0xfc00) != 0xd800 || (*p & 0xfc00) != 0xdc00) { - FMT_THROW(format_error("failed to format time")); - } - c = (c << 10) + static_cast(*p) - 0x35fdc00; - } - if (c < 0x80) { - buffer.push_back(static_cast(c)); - } else if (c < 0x800) { - buffer.push_back(static_cast(0xc0 | (c >> 6))); - buffer.push_back(static_cast(0x80 | (c & 0x3f))); - } else if ((c >= 0x800 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xffff)) { - buffer.push_back(static_cast(0xe0 | (c >> 12))); - buffer.push_back(static_cast(0x80 | ((c & 0xfff) >> 6))); - buffer.push_back(static_cast(0x80 | (c & 0x3f))); - } else if (c >= 0x10000 && c <= 0x10ffff) { - buffer.push_back(static_cast(0xf0 | (c >> 18))); - buffer.push_back(static_cast(0x80 | ((c & 0x3ffff) >> 12))); - buffer.push_back(static_cast(0x80 | ((c & 0xfff) >> 6))); - buffer.push_back(static_cast(0x80 | (c & 0x3f))); - } else { - FMT_THROW(format_error("failed to format time")); - } - } - } - return copy_str(buffer.data(), buffer.data() + buffer.size(), out); + auto&& buf = basic_memory_buffer(); + do_write(buf, time, loc, format, modifier); + return write_encoded_tm_str(out, string_view(buf.data(), buf.size()), loc); } } // namespace detail @@ -881,6 +918,12 @@ template struct has_member_data_tm_gmtoff> : std::true_type {}; +template +struct has_member_data_tm_zone : std::false_type {}; +template +struct has_member_data_tm_zone> + : std::true_type {}; + #if defined(_WIN32) inline void tzset_once() { static bool init = []() -> bool { @@ -1038,6 +1081,14 @@ template class tm_writer { #endif } + void format_tz_name_impl(std::true_type) { + if (is_classic_) + out_ = write_tm_str(out_, tm_.tm_zone, loc_); + else + format_localized('Z'); + } + void format_tz_name_impl(std::false_type) { format_localized('Z'); } + void format_localized(char format, char modifier = 0) { out_ = write(out_, tm_, loc_, format, modifier); } @@ -1147,7 +1198,7 @@ template class tm_writer { void on_utc_offset() { format_utc_offset_impl(has_member_data_tm_gmtoff{}); } - void on_tz_name() { format_localized('Z'); } + void on_tz_name() { format_tz_name_impl(has_member_data_tm_zone{}); } void on_year(numeric_system ns) { if (is_classic_ || ns == numeric_system::standard)