diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index 46590074b823..694845bc1272 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -407,6 +407,40 @@ auto write(OutputIt out, const std::tm& time, const std::locale& loc, return write_encoded_tm_str(out, string_view(buf.data(), buf.size()), loc); } +#if FMT_USE_LOCALE +inline auto is_classic_locale(locale_ref loc) -> bool { + return !loc || loc.get() == get_classic_locale(); +} + +template +auto write_localized_time(OutputIt out, const std::tm& time, locale_ref loc, + char format, char modifier) -> OutputIt { + return write(out, time, loc.get(), format, modifier); +} + +template +auto write_localized_str(OutputIt out, string_view sv, locale_ref loc) + -> OutputIt { + if (!loc) return write_tm_str(out, sv, get_classic_locale()); + return write_tm_str(out, sv, loc.get()); +} +#else +constexpr auto is_classic_locale(locale_ref) -> bool { return true; } + +// Never called because is_classic_locale() is always true. +template +auto write_localized_time(OutputIt out, const std::tm&, locale_ref, char, char) + -> OutputIt { + return out; +} + +// Zone names are ASCII, so there is nothing to transcode. +template +auto write_localized_str(OutputIt out, string_view sv, locale_ref) -> OutputIt { + return copy(sv.data(), sv.data() + sv.size(), out); +} +#endif // FMT_USE_LOCALE + template using is_similar_arithmetic_type = bool_constant<(std::is_integral::value && std::is_integral::value) || @@ -1045,7 +1079,7 @@ class tm_writer { private: static constexpr int days_per_week = 7; - const std::locale& loc_; + locale_ref loc_; bool is_classic_; OutputIt out_; const Duration* subsecs_; @@ -1192,7 +1226,7 @@ class tm_writer { template ::value)> void format_tz_name(const T& tm) { if (!tm.tm_zone) FMT_THROW(format_error("no timezone")); - out_ = write_tm_str(out_, tm.tm_zone, loc_); + out_ = write_localized_str(out_, tm.tm_zone, loc_); } template ::value)> void format_tz_name(const T&) { @@ -1200,14 +1234,14 @@ class tm_writer { } void format_localized(char format, char modifier = 0) { - out_ = write(out_, tm_, loc_, format, modifier); + out_ = write_localized_time(out_, tm_, loc_, format, modifier); } public: - tm_writer(const std::locale& loc, OutputIt out, const std::tm& tm, + tm_writer(locale_ref loc, OutputIt out, const std::tm& tm, const Duration* subsecs = nullptr) : loc_(loc), - is_classic_(loc_ == get_classic_locale()), + is_classic_(is_classic_locale(loc)), out_(out), subsecs_(subsecs), tm_(tm) {} @@ -1583,31 +1617,6 @@ auto format_duration_unit(OutputIt out) -> OutputIt { return out; } -class get_locale { - private: - union { - std::locale locale_; - }; - bool has_locale_ = false; - - public: - inline get_locale(bool localized, locale_ref loc) : has_locale_(localized) { - if (!localized) return; - ignore_unused(loc); - ::new (&locale_) std::locale( -#if FMT_USE_LOCALE - loc.template get() -#endif - ); - } - inline ~get_locale() { - if (has_locale_) locale_.~locale(); - } - inline operator const std::locale&() const { - return has_locale_ ? locale_ : get_classic_locale(); - } -}; - template struct duration_formatter { using iterator = basic_appender; @@ -1702,8 +1711,7 @@ struct duration_formatter { template void format_tm(const tm& time, Callback cb, Args... args) { if (isnan(val)) return write_nan(); - get_locale loc(localized, locale); - auto w = tm_writer_type(loc, out, time); + auto w = tm_writer_type(localized ? locale : locale_ref(), out, time); (w.*cb)(args...); out = w.out(); } @@ -1922,7 +1930,7 @@ struct formatter : private formatter { auto time = std::tm(); time.tm_wday = static_cast(wd.c_encoding()); if (use_tm_formatter_) return formatter::format(time, ctx); - detail::get_locale loc(this->localized(), ctx.locale()); + auto loc = this->localized() ? ctx.locale() : locale_ref(); auto w = detail::tm_writer(loc, ctx.out(), time); w.on_abbr_weekday(); return w.out(); @@ -1946,7 +1954,7 @@ struct formatter : private formatter { auto time = std::tm(); time.tm_mday = static_cast(static_cast(d)); if (use_tm_formatter_) return formatter::format(time, ctx); - detail::get_locale loc(false, ctx.locale()); + auto loc = locale_ref(); auto w = detail::tm_writer(loc, ctx.out(), time); w.on_day_of_month(detail::numeric_system::standard, detail::pad_type::zero); return w.out(); @@ -1974,7 +1982,7 @@ struct formatter : private formatter { auto time = std::tm(); time.tm_mon = static_cast(static_cast(m)) - 1; if (use_tm_formatter_) return formatter::format(time, ctx); - detail::get_locale loc(this->localized(), ctx.locale()); + auto loc = this->localized() ? ctx.locale() : locale_ref(); auto w = detail::tm_writer(loc, ctx.out(), time); w.on_abbr_month(); return w.out(); @@ -1998,7 +2006,7 @@ struct formatter : private formatter { auto time = std::tm(); time.tm_year = static_cast(y) - 1900; if (use_tm_formatter_) return formatter::format(time, ctx); - detail::get_locale loc(false, ctx.locale()); + auto loc = locale_ref(); auto w = detail::tm_writer(loc, ctx.out(), time); w.on_year(detail::numeric_system::standard, detail::pad_type::zero); return w.out(); @@ -2025,7 +2033,7 @@ struct formatter : private formatter { time.tm_mon = static_cast(static_cast(val.month())) - 1; time.tm_mday = static_cast(static_cast(val.day())); if (use_tm_formatter_) return formatter::format(time, ctx); - detail::get_locale loc(true, ctx.locale()); + auto loc = ctx.locale(); auto w = detail::tm_writer(loc, ctx.out(), time); w.on_iso_date(); return w.out(); @@ -2144,8 +2152,7 @@ template struct formatter { detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_, ctx); - auto loc_ref = specs.localized() ? ctx.locale() : locale_ref(); - detail::get_locale loc(static_cast(loc_ref), loc_ref); + auto loc = specs.localized() ? ctx.locale() : locale_ref(); auto w = detail::tm_writer, Char, Duration>( loc, out, tm, subsecs); detail::parse_chrono_format(fmt_.begin(), fmt_.end(), w);