From 19e3e2fc7350a6b4971dcb7b49696c537dce0921 Mon Sep 17 00:00:00 2001 From: Vladislav Shchapov Date: Thu, 11 Nov 2021 21:33:33 +0500 Subject: [PATCH 1/2] Use predefined formats for C-locale --- include/fmt/chrono.h | 113 +++++++++++++++++++++++++------------------ 1 file changed, 67 insertions(+), 46 deletions(-) diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index b6a9a7907af6..ba4e6ad72550 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -1018,13 +1018,16 @@ template class tm_writer { format_localized('A'); } void on_dec0_weekday(numeric_system ns) { - if (ns != numeric_system::standard) return format_localized('w', 'O'); - write1(tm_wday()); + if (is_classic_ || ns == numeric_system::standard) return write1(tm_wday()); + format_localized('w', 'O'); } void on_dec1_weekday(numeric_system ns) { - if (ns != numeric_system::standard) return format_localized('u', 'O'); - auto wday = tm_wday(); - write1(wday == 0 ? days_per_week : wday); + if (is_classic_ || ns == numeric_system::standard) { + auto wday = tm_wday(); + write1(wday == 0 ? days_per_week : wday); + } else { + format_localized('u', 'O'); + } } void on_abbr_month() { @@ -1095,49 +1098,63 @@ template class tm_writer { void on_tz_name() { format_localized('Z'); } void on_year(numeric_system ns) { - if (ns != numeric_system::standard) return format_localized('Y', 'E'); - write_year(tm_year()); + if (is_classic_ || ns == numeric_system::standard) + return write_year(tm_year()); + format_localized('Y', 'E'); } void on_short_year(numeric_system ns) { - if (ns != numeric_system::standard) return format_localized('y', 'O'); - write2(split_year_lower(tm_year())); + if (is_classic_ || ns == numeric_system::standard) + return write2(split_year_lower(tm_year())); + format_localized('y', 'O'); + } + void on_offset_year() { + if (is_classic_) return write2(split_year_lower(tm_year())); + format_localized('y', 'E'); } - void on_offset_year() { format_localized('y', 'E'); } void on_century(numeric_system ns) { - if (ns != numeric_system::standard) return format_localized('C', 'E'); - auto year = tm_year(); - auto upper = year / 100; - if (year >= -99 && year < 0) { - // Zero upper on negative year. - *out_++ = '-'; - *out_++ = '0'; - } else if (upper >= 0 && upper < 100) { - write2(static_cast(upper)); + if (is_classic_ || ns == numeric_system::standard) { + auto year = tm_year(); + auto upper = year / 100; + if (year >= -99 && year < 0) { + // Zero upper on negative year. + *out_++ = '-'; + *out_++ = '0'; + } else if (upper >= 0 && upper < 100) { + write2(static_cast(upper)); + } else { + out_ = write(out_, upper); + } } else { - out_ = write(out_, upper); + format_localized('C', 'E'); } } void on_dec_month(numeric_system ns) { - if (ns != numeric_system::standard) return format_localized('m', 'O'); - write2(tm_mon() + 1); + if (is_classic_ || ns == numeric_system::standard) + return write2(tm_mon() + 1); + format_localized('m', 'O'); } void on_dec0_week_of_year(numeric_system ns) { - if (ns != numeric_system::standard) return format_localized('U', 'O'); - write2((tm_yday() + days_per_week - tm_wday()) / days_per_week); + if (is_classic_ || ns == numeric_system::standard) + return write2((tm_yday() + days_per_week - tm_wday()) / days_per_week); + format_localized('U', 'O'); } void on_dec1_week_of_year(numeric_system ns) { - if (ns != numeric_system::standard) return format_localized('W', 'O'); - auto wday = tm_wday(); - write2((tm_yday() + days_per_week - - (wday == 0 ? (days_per_week - 1) : (wday - 1))) / - days_per_week); + if (is_classic_ || ns == numeric_system::standard) { + auto wday = tm_wday(); + write2((tm_yday() + days_per_week - + (wday == 0 ? (days_per_week - 1) : (wday - 1))) / + days_per_week); + } else { + format_localized('W', 'O'); + } } void on_iso_week_of_year(numeric_system ns) { - if (ns != numeric_system::standard) return format_localized('V', 'O'); - write2(tm_iso_week_of_year()); + if (is_classic_ || ns == numeric_system::standard) + return write2(tm_iso_week_of_year()); + format_localized('V', 'O'); } void on_iso_week_based_year() { write_year(tm_iso_week_year()); } @@ -1151,32 +1168,36 @@ template class tm_writer { write2(yday % 100); } void on_day_of_month(numeric_system ns) { - if (ns != numeric_system::standard) return format_localized('d', 'O'); - write2(tm_mday()); + if (is_classic_ || ns == numeric_system::standard) return write2(tm_mday()); + format_localized('d', 'O'); } void on_day_of_month_space(numeric_system ns) { - if (ns != numeric_system::standard) return format_localized('e', 'O'); - auto mday = to_unsigned(tm_mday()) % 100; - const char* d2 = digits2(mday); - *out_++ = mday < 10 ? ' ' : d2[0]; - *out_++ = d2[1]; + if (is_classic_ || ns == numeric_system::standard) { + auto mday = to_unsigned(tm_mday()) % 100; + const char* d2 = digits2(mday); + *out_++ = mday < 10 ? ' ' : d2[0]; + *out_++ = d2[1]; + } else { + format_localized('e', 'O'); + } } void on_24_hour(numeric_system ns) { - if (ns != numeric_system::standard) return format_localized('H', 'O'); - write2(tm_hour()); + if (is_classic_ || ns == numeric_system::standard) return write2(tm_hour()); + format_localized('H', 'O'); } void on_12_hour(numeric_system ns) { - if (ns != numeric_system::standard) return format_localized('I', 'O'); - write2(tm_hour12()); + if (is_classic_ || ns == numeric_system::standard) + return write2(tm_hour12()); + format_localized('I', 'O'); } void on_minute(numeric_system ns) { - if (ns != numeric_system::standard) return format_localized('M', 'O'); - write2(tm_min()); + if (is_classic_ || ns == numeric_system::standard) return write2(tm_min()); + format_localized('M', 'O'); } void on_second(numeric_system ns) { - if (ns != numeric_system::standard) return format_localized('S', 'O'); - write2(tm_sec()); + if (is_classic_ || ns == numeric_system::standard) return write2(tm_sec()); + format_localized('S', 'O'); } void on_12_hour_time() { From d7b90d77c05d9fcab3dc97d7026f147e44121882 Mon Sep 17 00:00:00 2001 From: Vladislav Shchapov Date: Sat, 13 Nov 2021 14:31:57 +0500 Subject: [PATCH 2/2] Add platform-specific 'z' formatter --- include/fmt/chrono.h | 51 +++++++++++++++++++++++++++++++++++++++++++- test/chrono-test.cc | 7 ++++++ test/xchar-test.cc | 7 ++++++ 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index ba4e6ad72550..d54c8ae79ec0 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 fc773c531c75..1d0052f54b5e 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 8f98c0504882..346cd212620e 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) {