diff --git a/stl/inc/chrono b/stl/inc/chrono index e3f887fa27e..1d55781c24b 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -5239,6 +5239,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 @@ -5294,6 +5295,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') { @@ -5375,6 +5380,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")); } @@ -5582,7 +5595,7 @@ namespace chrono { template 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(_Val))); } @@ -5595,7 +5608,7 @@ namespace chrono { template 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())); } @@ -5604,40 +5617,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 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 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 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 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 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 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 @@ -5648,25 +5662,26 @@ namespace chrono { template 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 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 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 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) @@ -5715,9 +5730,7 @@ namespace chrono { requires (!treat_as_floating_point_v && _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(_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 @@ -5727,22 +5740,22 @@ namespace chrono { template 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 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 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 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 @@ -5755,7 +5768,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 @@ -5899,10 +5912,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{'-'}; diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index fb4adb47f77..265f20ab940 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -260,6 +260,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() diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_formatting/test.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_formatting/test.cpp index 55f162397d7..e05e874a8df 100644 --- a/tests/std/tests/P0355R7_calendars_and_time_zones_formatting/test.cpp +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_formatting/test.cpp @@ -48,6 +48,7 @@ struct testing_callbacks { int expected_precision = -1; size_t expected_dynamic_precision = static_cast(-1); bool expected_auto_dynamic_precision = false; + bool expected_localized = false; vector<_Chrono_spec>& 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(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 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 v_empty{}; + test_parse_helper(parse_chrono_format_specs_fn, s9, false, view_typ::npos, + {.expected_localized = true, .expected_chrono_specs = v_empty}); + + vector 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 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 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() {