Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 46 additions & 39 deletions include/fmt/chrono.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::locale>() == get_classic_locale();
}

template <typename Char, typename OutputIt>
auto write_localized_time(OutputIt out, const std::tm& time, locale_ref loc,
char format, char modifier) -> OutputIt {
return write<Char>(out, time, loc.get<std::locale>(), format, modifier);
}

template <typename Char, typename OutputIt>
auto write_localized_str(OutputIt out, string_view sv, locale_ref loc)
-> OutputIt {
if (!loc) return write_tm_str<Char>(out, sv, get_classic_locale());
return write_tm_str<Char>(out, sv, loc.get<std::locale>());
}
#else
constexpr auto is_classic_locale(locale_ref) -> bool { return true; }

// Never called because is_classic_locale() is always true.
template <typename Char, typename OutputIt>
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 <typename Char, typename OutputIt>
auto write_localized_str(OutputIt out, string_view sv, locale_ref) -> OutputIt {
return copy<Char>(sv.data(), sv.data() + sv.size(), out);
}
#endif // FMT_USE_LOCALE

template <typename T, typename U>
using is_similar_arithmetic_type =
bool_constant<(std::is_integral<T>::value && std::is_integral<U>::value) ||
Expand Down Expand Up @@ -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_;
Expand Down Expand Up @@ -1192,22 +1226,22 @@ class tm_writer {
template <typename T, FMT_ENABLE_IF(has_tm_zone<T>::value)>
void format_tz_name(const T& tm) {
if (!tm.tm_zone) FMT_THROW(format_error("no timezone"));
out_ = write_tm_str<Char>(out_, tm.tm_zone, loc_);
out_ = write_localized_str<Char>(out_, tm.tm_zone, loc_);
}
template <typename T, FMT_ENABLE_IF(!has_tm_zone<T>::value)>
void format_tz_name(const T&) {
out_ = std::copy_n(utc(), 3, out_);
}

void format_localized(char format, char modifier = 0) {
out_ = write<Char>(out_, tm_, loc_, format, modifier);
out_ = write_localized_time<Char>(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) {}
Expand Down Expand Up @@ -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<std::locale>()
#endif
);
}
inline ~get_locale() {
if (has_locale_) locale_.~locale();
}
inline operator const std::locale&() const {
return has_locale_ ? locale_ : get_classic_locale();
}
};

template <typename Char, typename Rep, typename Period>
struct duration_formatter {
using iterator = basic_appender<Char>;
Expand Down Expand Up @@ -1702,8 +1711,7 @@ struct duration_formatter {
template <typename Callback, typename... Args>
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();
}
Expand Down Expand Up @@ -1922,7 +1930,7 @@ struct formatter<weekday, Char> : private formatter<std::tm, Char> {
auto time = std::tm();
time.tm_wday = static_cast<int>(wd.c_encoding());
if (use_tm_formatter_) return formatter<std::tm, Char>::format(time, ctx);
detail::get_locale loc(this->localized(), ctx.locale());
auto loc = this->localized() ? ctx.locale() : locale_ref();
auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), time);
w.on_abbr_weekday();
return w.out();
Expand All @@ -1946,7 +1954,7 @@ struct formatter<day, Char> : private formatter<std::tm, Char> {
auto time = std::tm();
time.tm_mday = static_cast<int>(static_cast<unsigned>(d));
if (use_tm_formatter_) return formatter<std::tm, Char>::format(time, ctx);
detail::get_locale loc(false, ctx.locale());
auto loc = locale_ref();
auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), time);
w.on_day_of_month(detail::numeric_system::standard, detail::pad_type::zero);
return w.out();
Expand Down Expand Up @@ -1974,7 +1982,7 @@ struct formatter<month, Char> : private formatter<std::tm, Char> {
auto time = std::tm();
time.tm_mon = static_cast<int>(static_cast<unsigned>(m)) - 1;
if (use_tm_formatter_) return formatter<std::tm, Char>::format(time, ctx);
detail::get_locale loc(this->localized(), ctx.locale());
auto loc = this->localized() ? ctx.locale() : locale_ref();
auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), time);
w.on_abbr_month();
return w.out();
Expand All @@ -1998,7 +2006,7 @@ struct formatter<year, Char> : private formatter<std::tm, Char> {
auto time = std::tm();
time.tm_year = static_cast<int>(y) - 1900;
if (use_tm_formatter_) return formatter<std::tm, Char>::format(time, ctx);
detail::get_locale loc(false, ctx.locale());
auto loc = locale_ref();
auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), time);
w.on_year(detail::numeric_system::standard, detail::pad_type::zero);
return w.out();
Expand All @@ -2025,7 +2033,7 @@ struct formatter<year_month_day, Char> : private formatter<std::tm, Char> {
time.tm_mon = static_cast<int>(static_cast<unsigned>(val.month())) - 1;
time.tm_mday = static_cast<int>(static_cast<unsigned>(val.day()));
if (use_tm_formatter_) return formatter<std::tm, Char>::format(time, ctx);
detail::get_locale loc(true, ctx.locale());
auto loc = ctx.locale();
auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), time);
w.on_iso_date();
return w.out();
Expand Down Expand Up @@ -2144,8 +2152,7 @@ template <typename Char> struct formatter<std::tm, Char> {
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<bool>(loc_ref), loc_ref);
auto loc = specs.localized() ? ctx.locale() : locale_ref();
auto w = detail::tm_writer<basic_appender<Char>, Char, Duration>(
loc, out, tm, subsecs);
detail::parse_chrono_format(fmt_.begin(), fmt_.end(), w);
Expand Down
Loading