diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index 6fd21afbd069..b23bb1afc01c 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -747,7 +747,7 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin, handler.on_duration_unit(); break; case 'z': - handler.on_utc_offset(); + handler.on_utc_offset(numeric_system::standard); break; case 'Z': handler.on_tz_name(); @@ -775,6 +775,9 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin, case 'X': handler.on_loc_time(numeric_system::alternative); break; + case 'z': + handler.on_utc_offset(numeric_system::alternative); + break; default: FMT_THROW(format_error("invalid format")); } @@ -823,6 +826,9 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin, case 'S': handler.on_second(numeric_system::alternative); break; + case 'z': + handler.on_utc_offset(numeric_system::alternative); + break; default: FMT_THROW(format_error("invalid format")); } @@ -874,7 +880,7 @@ template struct null_chrono_spec_handler { FMT_CONSTEXPR void on_am_pm() { unsupported(); } FMT_CONSTEXPR void on_duration_value() { unsupported(); } FMT_CONSTEXPR void on_duration_unit() { unsupported(); } - FMT_CONSTEXPR void on_utc_offset() { unsupported(); } + FMT_CONSTEXPR void on_utc_offset(numeric_system) { unsupported(); } FMT_CONSTEXPR void on_tz_name() { unsupported(); } }; @@ -915,7 +921,7 @@ struct tm_format_checker : null_chrono_spec_handler { FMT_CONSTEXPR void on_24_hour_time() {} FMT_CONSTEXPR void on_iso_time() {} FMT_CONSTEXPR void on_am_pm() {} - FMT_CONSTEXPR void on_utc_offset() {} + FMT_CONSTEXPR void on_utc_offset(numeric_system) {} FMT_CONSTEXPR void on_tz_name() {} }; @@ -1218,7 +1224,7 @@ class tm_writer { } } - void write_utc_offset(long offset) { + void write_utc_offset(long offset, numeric_system ns) { if (offset < 0) { *out_++ = '-'; offset = -offset; @@ -1227,14 +1233,15 @@ class tm_writer { } offset /= 60; write2(static_cast(offset / 60)); + if (ns != numeric_system::standard) *out_++ = ':'; write2(static_cast(offset % 60)); } template ::value)> - void format_utc_offset_impl(const T& tm) { - write_utc_offset(tm.tm_gmtoff); + void format_utc_offset_impl(const T& tm, numeric_system ns) { + write_utc_offset(tm.tm_gmtoff, ns); } template ::value)> - void format_utc_offset_impl(const T& tm) { + void format_utc_offset_impl(const T& tm, numeric_system ns) { #if defined(_WIN32) && defined(_UCRT) # if FMT_USE_TZSET tzset_once(); @@ -1246,10 +1253,17 @@ class tm_writer { _get_dstbias(&dstbias); offset += dstbias; } - write_utc_offset(-offset); + write_utc_offset(-offset, ns); #else - ignore_unused(tm); - format_localized('z'); + if (ns == numeric_system::standard) return format_localized('z'); + + // Extract timezone offset from timezone conversion functions. + std::tm gtm = tm; + std::time_t gt = std::mktime(>m); + std::tm ltm = gmtime(gt); + std::time_t lt = std::mktime(<m); + long offset = gt - lt; + write_utc_offset(offset, ns); #endif } @@ -1375,7 +1389,7 @@ class tm_writer { out_ = copy_str(std::begin(buf) + offset, std::end(buf), out_); } - void on_utc_offset() { format_utc_offset_impl(tm_); } + void on_utc_offset(numeric_system ns) { format_utc_offset_impl(tm_, ns); } void on_tz_name() { format_tz_name_impl(tm_); } void on_year(numeric_system ns) { @@ -1807,7 +1821,7 @@ struct chrono_formatter { void on_loc_time(numeric_system) {} void on_us_date() {} void on_iso_date() {} - void on_utc_offset() {} + void on_utc_offset(numeric_system) {} void on_tz_name() {} void on_year(numeric_system) {} void on_short_year(numeric_system) {} diff --git a/test/chrono-test.cc b/test/chrono-test.cc index 26591496744d..2321c64c17a1 100644 --- a/test/chrono-test.cc +++ b/test/chrono-test.cc @@ -7,6 +7,7 @@ #include "fmt/chrono.h" +#include #include #include @@ -290,6 +291,21 @@ TEST(chrono_test, time_point) { EXPECT_EQ(sys_output, fmt::format(fmt::runtime(fmt_spec), t1)); EXPECT_EQ(sys_output, fmt::format(fmt::runtime(fmt_spec), tm)); } + + if (std::find(spec_list.cbegin(), spec_list.cend(), "%z") != + spec_list.cend()) { + auto t = std::chrono::system_clock::to_time_t(t1); + auto tm = *std::localtime(&t); + + auto sys_output = system_strftime("%z", &tm); + sys_output.insert(sys_output.end() - 2, 1, ':'); + + EXPECT_EQ(sys_output, fmt::format("{:%Ez}", t1)); + EXPECT_EQ(sys_output, fmt::format("{:%Ez}", tm)); + + EXPECT_EQ(sys_output, fmt::format("{:%Oz}", t1)); + EXPECT_EQ(sys_output, fmt::format("{:%Oz}", tm)); + } } #ifndef FMT_STATIC_THOUSANDS_SEPARATOR