diff --git a/stl/inc/chrono b/stl/inc/chrono index e4850fd835f..12cae70079d 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -5265,24 +5265,25 @@ struct _Chrono_spec { }; template -struct _Chrono_format_specs { +struct _Chrono_format_specs2 { int _Width = 0; int _Precision = -1; int _Dynamic_width_index = -1; 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 vector<_Chrono_spec<_CharT>> _Chrono_specs_list; }; -// Model of _Chrono_parse_spec_callbacks that fills a _Chrono_format_specs with the parsed data +// Model of _Chrono_parse_spec_callbacks that fills a _Chrono_format_specs2 with the parsed data template class _Chrono_specs_setter { public: - constexpr explicit _Chrono_specs_setter(_Chrono_format_specs<_CharT>& _Specs_, _ParseContext& _Parse_ctx_) + constexpr explicit _Chrono_specs_setter(_Chrono_format_specs2<_CharT>& _Specs_, _ParseContext& _Parse_ctx_) : _Specs(_Specs_), _Parse_ctx(_Parse_ctx_) {} // same as _Specs_setter @@ -5327,6 +5328,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') { @@ -5347,7 +5352,7 @@ public: } private: - _Chrono_format_specs<_CharT>& _Specs; + _Chrono_format_specs2<_CharT>& _Specs; _ParseContext& _Parse_ctx; _NODISCARD static constexpr int _Verify_dynamic_arg_index_in_range(const size_t _Idx) { @@ -5408,6 +5413,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")); } @@ -5615,7 +5628,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))); } @@ -5628,7 +5641,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())); } @@ -5637,40 +5650,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}/{: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}/{:L}"), _Val.year(), _Val.month()); } template @@ -5681,25 +5695,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}/{: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}/{: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}/{: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) @@ -5750,7 +5765,7 @@ namespace chrono { // 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}); + _Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L} {:L}"), year_month_day{_Dp}, hh_mm_ss{_Val - _Dp}); } template @@ -5760,22 +5775,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 @@ -5788,7 +5803,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 @@ -5932,10 +5947,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{'-'}; @@ -6294,7 +6309,7 @@ namespace chrono { return _Fmt_str; } - _Chrono_format_specs<_CharT> _Specs{}; + _Chrono_format_specs2<_CharT> _Specs{}; basic_string_view<_CharT> _Time_zone_abbreviation{}; }; } // namespace chrono 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 b16ef15005f..7cc28fa2c9f 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 @@ -39,6 +39,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; @@ -66,6 +67,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); @@ -884,6 +888,34 @@ void test_zoned_time_formatter() { assert(format(STR("{:%g %G %U %V %W}"), zt) == STR("21 2021 16 16 16")); } +template +void test_localized() { + 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) { + std::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() { test_parse_conversion_spec(); test_parse_conversion_spec(); @@ -955,6 +987,9 @@ void test() { test_zoned_time_formatter(); test_zoned_time_formatter(); + + test_localized(); + test_localized(); } int main() {