diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index ba4e6ad7..d54c8ae7 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -13,6 +13,7 @@ #include #include #include +#include #include "format.h" @@ -871,6 +872,22 @@ inline const char* tm_mon_short_name(int mon) { return mon >= 0 && mon <= 11 ? short_name_list[mon] : "???"; } +template +struct has_member_data_tm_gmtoff : std::false_type {}; +template +struct has_member_data_tm_gmtoff> + : std::true_type {}; + +#if defined(_WIN32) +inline void tzset_once() { + static bool init = []() -> bool { + _tzset(); + return true; + }(); + ignore_unused(init); +} +#endif + template class tm_writer { private: static constexpr int days_per_week = 7; @@ -988,6 +1005,36 @@ template class tm_writer { } } + void write_utc_offset(long offset) { + if (offset < 0) { + *out_++ = '-'; + offset = -offset; + } else { + *out_++ = '+'; + } + offset /= 60; + write2(static_cast(offset / 60)); + write2(static_cast(offset % 60)); + } + void format_utc_offset_impl(std::true_type) { + write_utc_offset(tm_.tm_gmtoff); + } + void format_utc_offset_impl(std::false_type) { +#if defined(_WIN32) + tzset_once(); + long offset = 0; + _get_timezone(&offset); + if (tm_.tm_isdst) { + long dstbias = 0; + _get_dstbias(&dstbias); + offset += dstbias; + } + write_utc_offset(-offset); +#else + format_localized('z'); +#endif + } + void format_localized(char format, char modifier = 0) { out_ = write(out_, tm_, loc_, format, modifier); } @@ -1094,7 +1141,9 @@ template class tm_writer { out_ = copy_str(std::begin(buf) + offset, std::end(buf), out_); } - void on_utc_offset() { format_localized('z'); } + void on_utc_offset() { + format_utc_offset_impl(has_member_data_tm_gmtoff{}); + } void on_tz_name() { format_localized('Z'); } void on_year(numeric_system ns) { diff --git a/test/chrono-test.cc b/test/chrono-test.cc index fc773c53..1d0052f5 100644 --- a/test/chrono-test.cc +++ b/test/chrono-test.cc @@ -49,7 +49,14 @@ std::string system_strftime(const std::string& format, const std::tm* timeptr, os.imbue(loc); facet.put(os, os, ' ', timeptr, format.c_str(), format.c_str() + format.size()); +#ifdef _WIN32 + // Workaround a bug in older versions of Universal CRT. + auto str = os.str(); + if (str == "-0000") str = "+0000"; + return str; +#else return os.str(); +#endif } FMT_CONSTEXPR std::tm make_tm(int year, int mon, int mday, int hour, int min, diff --git a/test/xchar-test.cc b/test/xchar-test.cc index 8f98c050..346cd212 100644 --- a/test/xchar-test.cc +++ b/test/xchar-test.cc @@ -277,7 +277,14 @@ std::wstring system_wcsftime(const std::wstring& format, const std::tm* timeptr, os.imbue(loc); facet.put(os, os, L' ', timeptr, format.c_str(), format.c_str() + format.size()); +#ifdef _WIN32 + // Workaround a bug in older versions of Universal CRT. + auto str = os.str(); + if (str == L"-0000") str = L"+0000"; + return str; +#else return os.str(); +#endif } TEST(chrono_test, time_point) {