Skip to content

Commit

Permalink
Use tm.tm_zone
Browse files Browse the repository at this point in the history
  • Loading branch information
phprus committed Nov 27, 2021
1 parent 26e7511 commit 16faf54
Showing 1 changed file with 98 additions and 50 deletions.
148 changes: 98 additions & 50 deletions include/fmt/chrono.h
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,11 @@ 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 <typename Char>
inline void do_write(buffer<Char>& buf, const std::tm& time,
const std::locale& loc, char format, char modifier) {
Expand All @@ -303,26 +308,32 @@ inline void do_write(buffer<Char>& buf, const std::tm& time,
if (end.failed()) FMT_THROW(format_error("failed to format time"));
}

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&& buffer = get_buffer<Char>(out);
do_write<Char>(buffer, time, loc, format, modifier);
return buffer.out();
}

inline const std::locale& get_classic_locale() {
static const auto& locale = std::locale::classic();
return locale;
template <typename CodeUnit>
inline auto do_write_codecvt(CodeUnit* unit_buf, size_t unit_buf_size,
string_view in_buf, const std::locale& loc)
-> CodeUnit* {
using codecvt = std::codecvt<CodeUnit, char, std::mbstate_t>;
#if FMT_CLANG_VERSION
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated"
auto& f = std::use_facet<codecvt>(loc);
# pragma clang diagnostic pop
#else
auto& f = std::use_facet<codecvt>(loc);
#endif
auto mb = std::mbstate_t();
const char* from_next = nullptr;
CodeUnit* to_next = nullptr;
auto result = f.in(mb, in_buf.begin(), in_buf.end(), from_next, unit_buf,
unit_buf + unit_buf_size, to_next);
if (result != std::codecvt_base::ok)
FMT_THROW(format_error("failed to format time"));
return to_next;
}

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&& buffer = basic_memory_buffer<Char>();
do_write<char>(buffer, time, loc, format, modifier);
template <typename OutputIt>
auto do_write_char_buffer(OutputIt out, buffer<char>& buf,
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.
Expand All @@ -335,27 +346,13 @@ auto write(OutputIt out, const std::tm& time, const std::locale& loc,
using code_unit = char32_t;
#endif

using codecvt = std::codecvt<code_unit, char, std::mbstate_t>;
#if FMT_CLANG_VERSION
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated"
auto& f = std::use_facet<codecvt>(loc);
# pragma clang diagnostic pop
#else
auto& f = std::use_facet<codecvt>(loc);
#endif
constexpr size_t unit_buf_size = 32;
code_unit unit_buf[unit_buf_size] = {};
auto to_next = do_write_codecvt(unit_buf, unit_buf_size,
string_view(buf.data(), buf.size()), 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, 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) {
buf.clear();
for (code_unit* p = unit_buf; p != to_next; ++p) {
uint32_t c = static_cast<uint32_t>(*p);
if (sizeof(code_unit) == 2 && c >= 0xd800 && c <= 0xdfff) {
// surrogate pair
Expand All @@ -366,25 +363,62 @@ auto write(OutputIt out, const std::tm& time, const std::locale& loc,
c = (c << 10) + static_cast<uint32_t>(*p) - 0x35fdc00;
}
if (c < 0x80) {
buffer.push_back(static_cast<char>(c));
buf.push_back(static_cast<char>(c));
} else if (c < 0x800) {
buffer.push_back(static_cast<char>(0xc0 | (c >> 6)));
buffer.push_back(static_cast<char>(0x80 | (c & 0x3f)));
buf.push_back(static_cast<char>(0xc0 | (c >> 6)));
buf.push_back(static_cast<char>(0x80 | (c & 0x3f)));
} else if ((c >= 0x800 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xffff)) {
buffer.push_back(static_cast<char>(0xe0 | (c >> 12)));
buffer.push_back(static_cast<char>(0x80 | ((c & 0xfff) >> 6)));
buffer.push_back(static_cast<char>(0x80 | (c & 0x3f)));
buf.push_back(static_cast<char>(0xe0 | (c >> 12)));
buf.push_back(static_cast<char>(0x80 | ((c & 0xfff) >> 6)));
buf.push_back(static_cast<char>(0x80 | (c & 0x3f)));
} else if (c >= 0x10000 && c <= 0x10ffff) {
buffer.push_back(static_cast<char>(0xf0 | (c >> 18)));
buffer.push_back(static_cast<char>(0x80 | ((c & 0x3ffff) >> 12)));
buffer.push_back(static_cast<char>(0x80 | ((c & 0xfff) >> 6)));
buffer.push_back(static_cast<char>(0x80 | (c & 0x3f)));
buf.push_back(static_cast<char>(0xf0 | (c >> 18)));
buf.push_back(static_cast<char>(0x80 | ((c & 0x3ffff) >> 12)));
buf.push_back(static_cast<char>(0x80 | ((c & 0xfff) >> 6)));
buf.push_back(static_cast<char>(0x80 | (c & 0x3f)));
} else {
FMT_THROW(format_error("failed to format time"));
}
}
}
return copy_str<Char>(buffer.data(), buffer.data() + buffer.size(), out);
return copy_str<char>(buf.data(), buf.data() + buf.size(), out);
}

template <typename Char, typename OutputIt,
FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
auto write_tm_str(OutputIt out, string_view sv, const std::locale& loc)
-> OutputIt {
constexpr size_t unit_buf_size = 32;
Char unit_buf[unit_buf_size] = {};
auto to_next = do_write_codecvt(unit_buf, unit_buf_size, sv, loc);
return copy_str<Char>(unit_buf, to_next, out);
}

template <typename Char, typename OutputIt,
FMT_ENABLE_IF(std::is_same<Char, char>::value)>
auto write_tm_str(OutputIt out, string_view sv, const std::locale& loc)
-> OutputIt {
auto&& buf = basic_memory_buffer<Char>();
buf.append(sv.begin(), sv.end());
return do_write_char_buffer(out, buf, loc);
}

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&& buffer = get_buffer<Char>(out);
do_write<Char>(buffer, time, loc, format, modifier);
return buffer.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&& buffer = basic_memory_buffer<Char>();
do_write<char>(buffer, time, loc, format, modifier);
return do_write_char_buffer(out, buffer, loc);
}

} // namespace detail
Expand Down Expand Up @@ -881,6 +915,12 @@ template <typename T>
struct has_member_data_tm_gmtoff<T, void_t<decltype(T::tm_gmtoff)>>
: std::true_type {};

template <typename T, typename = void>
struct has_member_data_tm_zone : std::false_type {};
template <typename T>
struct has_member_data_tm_zone<T, void_t<decltype(T::tm_zone)>>
: std::true_type {};

#if defined(_WIN32)
inline void tzset_once() {
static bool init = []() -> bool {
Expand Down Expand Up @@ -1038,6 +1078,14 @@ template <typename OutputIt, typename Char> class tm_writer {
#endif
}

void format_tz_name_impl(std::true_type) {
if (is_classic_)
out_ = write_tm_str<Char>(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<Char>(out_, tm_, loc_, format, modifier);
}
Expand Down Expand Up @@ -1147,7 +1195,7 @@ template <typename OutputIt, typename Char> class tm_writer {
void on_utc_offset() {
format_utc_offset_impl(has_member_data_tm_gmtoff<std::tm>{});
}
void on_tz_name() { format_localized('Z'); }
void on_tz_name() { format_tz_name_impl(has_member_data_tm_zone<std::tm>{}); }

void on_year(numeric_system ns) {
if (is_classic_ || ns == numeric_system::standard)
Expand Down

0 comments on commit 16faf54

Please sign in to comment.