P2372R3: Fix handling of locale in chrono formatters (#2273)

Co-authored-by: Victor Zverovich <victor.zverovich@gmail.com>
Co-authored-by: Casey Carter <Casey@Carter.net>
Co-authored-by: Stephan T. Lavavej <stl@nuwen.net>
This commit is contained in:
Charlie Barto 2021-11-12 19:53:56 -08:00 коммит произвёл GitHub
Родитель dfa5326448
Коммит 785143a0c7
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
3 изменённых файлов: 80 добавлений и 25 удалений

Просмотреть файл

@ -5286,6 +5286,7 @@ struct _Chrono_format_specs {
int _Dynamic_precision_index = -1;
_Fmt_align _Alignment = _Fmt_align::_None;
uint8_t _Fill_length = 1;
bool _Localized = false;
// At most one codepoint (so one char32_t or four utf-8 char8_t)
_CharT _Fill[4 / sizeof(_CharT)] = {_CharT{' '}};
// recursive definition in grammar, so could have any number of these
@ -5341,6 +5342,10 @@ public:
_Specs._Dynamic_precision_index = _Verify_dynamic_arg_index_in_range(_Parse_ctx.next_arg_id());
}
constexpr void _On_localized() {
_Specs._Localized = true;
}
constexpr void _On_conversion_spec(char _Modifier, _CharT _Type) {
// NOTE: same performance note from _Basic_format_specs also applies here
if (_Modifier != '\0' && _Modifier != 'E' && _Modifier != 'O') {
@ -5422,6 +5427,14 @@ _NODISCARD constexpr const _CharT* _Parse_chrono_format_specs(
}
}
if (*_Begin == 'L') {
_Callbacks._On_localized();
++_Begin;
if (_Begin == _End) {
return _Begin;
}
}
if (*_Begin != '}' && *_Begin != '%') {
_THROW(format_error("Invalid format string - chrono-specs must begin with conversion-spec"));
}
@ -5629,7 +5642,7 @@ namespace chrono {
template <class _CharT, class _Traits>
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const month& _Val) {
return _Os << (_Val.ok() ? _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:%b}"), _Val)
return _Os << (_Val.ok() ? _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L%b}"), _Val)
: _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{} is not a valid month"),
static_cast<unsigned int>(_Val)));
}
@ -5642,7 +5655,7 @@ namespace chrono {
template <class _CharT, class _Traits>
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const weekday& _Val) {
return _Os << (_Val.ok() ? _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:%a}"), _Val)
return _Os << (_Val.ok() ? _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L%a}"), _Val)
: _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{} is not a valid weekday"),
_Val.c_encoding()));
}
@ -5651,40 +5664,41 @@ namespace chrono {
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const weekday_indexed& _Val) {
const auto _Idx = _Val.index();
return _Os << (_Idx >= 1 && _Idx <= 5
? _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{}[{}]"), _Val.weekday(), _Idx)
: _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{}[{} is not a valid index]"),
? _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L}[{}]"), _Val.weekday(), _Idx)
: _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L}[{} is not a valid index]"),
_Val.weekday(), _Idx));
}
template <class _CharT, class _Traits>
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const weekday_last& _Val) {
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{}[last]"), _Val.weekday());
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L}[last]"), _Val.weekday());
}
template <class _CharT, class _Traits>
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const month_day& _Val) {
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{}/{}"), _Val.month(), _Val.day());
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L}/{}"), _Val.month(), _Val.day());
}
template <class _CharT, class _Traits>
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const month_day_last& _Val) {
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{}/last"), _Val.month());
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L}/last"), _Val.month());
}
template <class _CharT, class _Traits>
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const month_weekday& _Val) {
return _Os << _STD format(
_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{}/{}"), _Val.month(), _Val.weekday_indexed());
_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L}/{:L}"), _Val.month(), _Val.weekday_indexed());
}
template <class _CharT, class _Traits>
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const month_weekday_last& _Val) {
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{}/{}"), _Val.month(), _Val.weekday_last());
return _Os << _STD format(
_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L}/{:L}"), _Val.month(), _Val.weekday_last());
}
template <class _CharT, class _Traits>
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const year_month& _Val) {
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{}/{}"), _Val.year(), _Val.month());
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{}/{:L}"), _Val.year(), _Val.month());
}
template <class _CharT, class _Traits>
@ -5695,25 +5709,26 @@ namespace chrono {
template <class _CharT, class _Traits>
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const year_month_day_last& _Val) {
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{}/{}"), _Val.year(), _Val.month_day_last());
return _Os << _STD format(
_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{}/{:L}"), _Val.year(), _Val.month_day_last());
}
template <class _CharT, class _Traits>
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const year_month_weekday& _Val) {
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{}/{}/{}"), _Val.year(), _Val.month(),
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{}/{:L}/{:L}"), _Val.year(), _Val.month(),
_Val.weekday_indexed());
}
template <class _CharT, class _Traits>
basic_ostream<_CharT, _Traits>& operator<<(
basic_ostream<_CharT, _Traits>& _Os, const year_month_weekday_last& _Val) {
return _Os << _STD format(
_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{}/{}/{}"), _Val.year(), _Val.month(), _Val.weekday_last());
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{}/{:L}/{:L}"), _Val.year(), _Val.month(),
_Val.weekday_last());
}
template <class _CharT, class _Traits, class _Duration>
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const hh_mm_ss<_Duration>& _Val) {
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:%T}"), _Val);
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L%T}"), _Val);
}
#pragma warning(push)
@ -5762,9 +5777,7 @@ namespace chrono {
requires (!treat_as_floating_point_v<typename _Duration::rep> && _Duration{1} < days{1})
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const sys_time<_Duration>& _Val) {
// clang-format on
const auto _Dp = _CHRONO floor<days>(_Val);
return _Os << _STD format(
_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{} {}"), year_month_day{_Dp}, hh_mm_ss{_Val - _Dp});
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L%F %T}"), _Val);
}
template <class _CharT, class _Traits>
@ -5774,22 +5787,22 @@ namespace chrono {
template <class _CharT, class _Traits, class _Duration>
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const utc_time<_Duration>& _Val) {
return _Os << _STD format(_STATICALLY_WIDEN(_CharT, "{:%F %T}"), _Val);
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L%F %T}"), _Val);
}
template <class _CharT, class _Traits, class _Duration>
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const tai_time<_Duration>& _Val) {
return _Os << _STD format(_STATICALLY_WIDEN(_CharT, "{:%F %T}"), _Val);
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L%F %T}"), _Val);
}
template <class _CharT, class _Traits, class _Duration>
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const gps_time<_Duration>& _Val) {
return _Os << _STD format(_STATICALLY_WIDEN(_CharT, "{:%F %T}"), _Val);
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L%F %T}"), _Val);
}
template <class _CharT, class _Traits, class _Duration>
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const file_time<_Duration>& _Val) {
return _Os << _STD format(_STATICALLY_WIDEN(_CharT, "{:%F %T}"), _Val);
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L%F %T}"), _Val);
}
template <class _CharT, class _Traits, class _Duration>
@ -5802,7 +5815,7 @@ namespace chrono {
basic_ostream<_CharT, _Traits>& _Os, const _Local_time_format_t<_Duration>& _Val) {
// Doesn't appear in the Standard, but allowed by N4885 [global.functions]/2.
// Implements N4885 [time.zone.zonedtime.nonmembers]/2 for zoned_time.
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:%F %T %Z}"), _Val);
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L%F %T %Z}"), _Val);
}
template <class _CharT, class _Traits, class _Duration, class _TimeZonePtr>
@ -5946,10 +5959,10 @@ namespace chrono {
_NODISCARD auto _Write(_FormatContext& _FormatCtx, const _Ty& _Val, const tm& _Time) {
basic_ostringstream<_CharT> _Stream;
_Stream.imbue(_Specs._Localized ? _FormatCtx.locale() : locale::classic());
if (_Specs._Chrono_specs_list.empty()) {
_Stream << _Val; // N4885 [time.format]/6
} else {
_Stream.imbue(_FormatCtx.locale());
if constexpr (_Is_specialization_v<_Ty, hh_mm_ss>) {
if (_Val.is_negative()) {
_Stream << _CharT{'-'};

Просмотреть файл

@ -264,6 +264,7 @@
// P2325R3 Views Should Not Be Required To Be Default Constructible
// P2328R1 join_view Should Join All views Of ranges
// P2367R0 Remove Misuses Of List-Initialization From Clause 24 Ranges
// P2372R3 Fixing Locale Handling In chrono Formatters
// P2432R1 Fix istream_view
// P????R? directory_entry::clear_cache()

Просмотреть файл

@ -48,6 +48,7 @@ struct testing_callbacks {
int expected_precision = -1;
size_t expected_dynamic_precision = static_cast<size_t>(-1);
bool expected_auto_dynamic_precision = false;
bool expected_localized = false;
vector<_Chrono_spec<CharT>>& expected_chrono_specs;
size_t curr_index = 0;
@ -75,6 +76,9 @@ struct testing_callbacks {
void _On_dynamic_precision(_Auto_id_tag) {
assert(expected_auto_dynamic_precision);
}
void _On_localized() {
assert(expected_localized);
}
void _On_conversion_spec(char mod, CharT type) {
assert(mod == expected_chrono_specs[curr_index]._Modifier);
assert(static_cast<char>(type) == expected_chrono_specs[curr_index]._Type);
@ -152,6 +156,8 @@ bool test_parse_chrono_format_specs() {
view_typ s6(STR("%H%"));
view_typ s7(STR("%H%}"));
view_typ s8(STR("%nB%tC%%D"));
view_typ s9(STR("L"));
view_typ s10(STR("L%F"));
vector<chrono_spec> v0{{._Modifier = 'O', ._Type = 'e'}};
test_parse_helper(parse_chrono_format_specs_fn, s0, false, s0.size(), {.expected_chrono_specs = v0});
@ -189,6 +195,15 @@ bool test_parse_chrono_format_specs() {
test_parse_helper(parse_chrono_format_specs_fn, s6, true, view_typ::npos, {.expected_chrono_specs = v});
test_parse_helper(parse_chrono_format_specs_fn, s7, true, view_typ::npos, {.expected_chrono_specs = v});
vector<chrono_spec> v_empty{};
test_parse_helper(parse_chrono_format_specs_fn, s9, false, view_typ::npos,
{.expected_localized = true, .expected_chrono_specs = v_empty});
vector<chrono_spec> v6{{._Type = 'F'}};
test_parse_helper(parse_chrono_format_specs_fn, s10, false, view_typ::npos,
{.expected_localized = true, .expected_chrono_specs = v6});
return true;
}
@ -900,8 +915,34 @@ void test_zoned_time_formatter() {
template <typename CharT>
void test_locale() {
assert(format(locale{"zh-CN"}, STR("{:^22%Y %B %d %A}"), 2021y / June / 16d)
assert(format(locale{"zh-CN"}, STR("{:^22L%Y %B %d %A}"), 2021y / June / 16d)
== STR(" 2021 \u516D\u6708 16 \u661F\u671F\u4E09 "));
locale loc("de-DE");
assert(format(loc, STR("{:%S}"), 42ms) == STR("00.042"));
assert(format(loc, STR("{:L%S}"), 42ms) == STR("00,042"));
auto stream = [=](auto value) {
basic_ostringstream<CharT> os;
os.imbue(loc);
os << value;
return os.str();
};
assert(stream(month{May}) == STR("Mai"));
assert(stream(weekday{Tuesday}) == STR("Di"));
assert(stream(weekday_indexed{Tuesday[3]}) == STR("Di[3]"));
assert(stream(weekday_indexed{Tuesday[42]}) == STR("Di[42 is not a valid index]"));
assert(stream(weekday_last{Tuesday}) == STR("Di[last]"));
assert(stream(month_day{May, day{4}}) == STR("Mai/04"));
assert(stream(month_day_last{May}) == STR("Mai/last"));
assert(stream(month_weekday{May / Tuesday[4]}) == STR("Mai/Di[4]"));
assert(stream(month_weekday_last{May / Tuesday[last]}) == STR("Mai/Di[last]"));
assert(stream(year_month{2021y / May}) == STR("2021/Mai"));
assert(stream(year_month_day_last{2021y / May / last}) == STR("2021/Mai/last"));
assert(stream(year_month_weekday{2021y / May / Tuesday[4]}) == STR("2021/Mai/Di[4]"));
assert(stream(year_month_weekday_last{2021y / May / Tuesday[last]}) == STR("2021/Mai/Di[last]"));
}
void test() {