Skip to content

Commit 7aedc78

Browse files
vitautCharles Barto
authored andcommitted
Fix handling of locale in chrono formatters
Add the 'L' specifier to chrono formatter Add a test for localized chrono formatting Use dash in locale name for compatibility with old systems ABI mitigation, drop std:: and auto Keep ostream insertion operators localized
1 parent 8833270 commit 7aedc78

File tree

2 files changed

+73
-28
lines changed
  • stl/inc
  • tests/std/tests/P0355R7_calendars_and_time_zones_formatting

2 files changed

+73
-28
lines changed

stl/inc/chrono

Lines changed: 42 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5232,24 +5232,25 @@ struct _Chrono_spec {
52325232
};
52335233

52345234
template <class _CharT>
5235-
struct _Chrono_format_specs {
5235+
struct _Chrono_format_specs2 {
52365236
int _Width = 0;
52375237
int _Precision = -1;
52385238
int _Dynamic_width_index = -1;
52395239
int _Dynamic_precision_index = -1;
52405240
_Fmt_align _Alignment = _Fmt_align::_None;
52415241
uint8_t _Fill_length = 1;
5242+
bool _Localized = false;
52425243
// At most one codepoint (so one char32_t or four utf-8 char8_t)
52435244
_CharT _Fill[4 / sizeof(_CharT)] = {_CharT{' '}};
52445245
// recursive definition in grammar, so could have any number of these
52455246
vector<_Chrono_spec<_CharT>> _Chrono_specs_list;
52465247
};
52475248

5248-
// Model of _Chrono_parse_spec_callbacks that fills a _Chrono_format_specs with the parsed data
5249+
// Model of _Chrono_parse_spec_callbacks that fills a _Chrono_format_specs2 with the parsed data
52495250
template <class _CharT, class _ParseContext>
52505251
class _Chrono_specs_setter {
52515252
public:
5252-
constexpr explicit _Chrono_specs_setter(_Chrono_format_specs<_CharT>& _Specs_, _ParseContext& _Parse_ctx_)
5253+
constexpr explicit _Chrono_specs_setter(_Chrono_format_specs2<_CharT>& _Specs_, _ParseContext& _Parse_ctx_)
52535254
: _Specs(_Specs_), _Parse_ctx(_Parse_ctx_) {}
52545255

52555256
// same as _Specs_setter
@@ -5294,6 +5295,10 @@ public:
52945295
_Specs._Dynamic_precision_index = _Verify_dynamic_arg_index_in_range(_Parse_ctx.next_arg_id());
52955296
}
52965297

5298+
constexpr void _On_localized() {
5299+
_Specs._Localized = true;
5300+
}
5301+
52975302
constexpr void _On_conversion_spec(char _Modifier, _CharT _Type) {
52985303
// NOTE: same performance note from _Basic_format_specs also applies here
52995304
if (_Modifier != '\0' && _Modifier != 'E' && _Modifier != 'O') {
@@ -5314,7 +5319,7 @@ public:
53145319
}
53155320

53165321
private:
5317-
_Chrono_format_specs<_CharT>& _Specs;
5322+
_Chrono_format_specs2<_CharT>& _Specs;
53185323
_ParseContext& _Parse_ctx;
53195324

53205325
_NODISCARD static constexpr int _Verify_dynamic_arg_index_in_range(const size_t _Idx) {
@@ -5375,6 +5380,14 @@ _NODISCARD constexpr const _CharT* _Parse_chrono_format_specs(
53755380
}
53765381
}
53775382

5383+
if (*_Begin == 'L') {
5384+
_Callbacks._On_localized();
5385+
++_Begin;
5386+
if (_Begin == _End) {
5387+
return _Begin;
5388+
}
5389+
}
5390+
53785391
if (*_Begin != '}' && *_Begin != '%') {
53795392
_THROW(format_error("Invalid format string - chrono-specs must begin with conversion-spec"));
53805393
}
@@ -5582,7 +5595,7 @@ namespace chrono {
55825595

55835596
template <class _CharT, class _Traits>
55845597
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const month& _Val) {
5585-
return _Os << (_Val.ok() ? _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:%b}"), _Val)
5598+
return _Os << (_Val.ok() ? _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L%b}"), _Val)
55865599
: _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{} is not a valid month"),
55875600
static_cast<unsigned int>(_Val)));
55885601
}
@@ -5595,7 +5608,7 @@ namespace chrono {
55955608

55965609
template <class _CharT, class _Traits>
55975610
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const weekday& _Val) {
5598-
return _Os << (_Val.ok() ? _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:%a}"), _Val)
5611+
return _Os << (_Val.ok() ? _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L%a}"), _Val)
55995612
: _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{} is not a valid weekday"),
56005613
_Val.c_encoding()));
56015614
}
@@ -5604,40 +5617,41 @@ namespace chrono {
56045617
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const weekday_indexed& _Val) {
56055618
const auto _Idx = _Val.index();
56065619
return _Os << (_Idx >= 1 && _Idx <= 5
5607-
? _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{}[{}]"), _Val.weekday(), _Idx)
5608-
: _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{}[{} is not a valid index]"),
5620+
? _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L}[{}]"), _Val.weekday(), _Idx)
5621+
: _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L}[{} is not a valid index]"),
56095622
_Val.weekday(), _Idx));
56105623
}
56115624

56125625
template <class _CharT, class _Traits>
56135626
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const weekday_last& _Val) {
5614-
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{}[last]"), _Val.weekday());
5627+
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L}[last]"), _Val.weekday());
56155628
}
56165629

56175630
template <class _CharT, class _Traits>
56185631
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const month_day& _Val) {
5619-
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{}/{}"), _Val.month(), _Val.day());
5632+
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L}/{:L}"), _Val.month(), _Val.day());
56205633
}
56215634

56225635
template <class _CharT, class _Traits>
56235636
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const month_day_last& _Val) {
5624-
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{}/last"), _Val.month());
5637+
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L}/last"), _Val.month());
56255638
}
56265639

56275640
template <class _CharT, class _Traits>
56285641
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const month_weekday& _Val) {
56295642
return _Os << _STD format(
5630-
_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{}/{}"), _Val.month(), _Val.weekday_indexed());
5643+
_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L}/{:L}"), _Val.month(), _Val.weekday_indexed());
56315644
}
56325645

56335646
template <class _CharT, class _Traits>
56345647
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const month_weekday_last& _Val) {
5635-
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{}/{}"), _Val.month(), _Val.weekday_last());
5648+
return _Os << _STD format(
5649+
_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L}/{:L}"), _Val.month(), _Val.weekday_last());
56365650
}
56375651

56385652
template <class _CharT, class _Traits>
56395653
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const year_month& _Val) {
5640-
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{}/{}"), _Val.year(), _Val.month());
5654+
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L}/{:L}"), _Val.year(), _Val.month());
56415655
}
56425656

56435657
template <class _CharT, class _Traits>
@@ -5648,25 +5662,26 @@ namespace chrono {
56485662

56495663
template <class _CharT, class _Traits>
56505664
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const year_month_day_last& _Val) {
5651-
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{}/{}"), _Val.year(), _Val.month_day_last());
5665+
return _Os << _STD format(
5666+
_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L}/{:L}"), _Val.year(), _Val.month_day_last());
56525667
}
56535668

56545669
template <class _CharT, class _Traits>
56555670
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const year_month_weekday& _Val) {
5656-
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{}/{}/{}"), _Val.year(), _Val.month(),
5671+
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L}/{:L}/{:L}"), _Val.year(), _Val.month(),
56575672
_Val.weekday_indexed());
56585673
}
56595674

56605675
template <class _CharT, class _Traits>
56615676
basic_ostream<_CharT, _Traits>& operator<<(
56625677
basic_ostream<_CharT, _Traits>& _Os, const year_month_weekday_last& _Val) {
5663-
return _Os << _STD format(
5664-
_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{}/{}/{}"), _Val.year(), _Val.month(), _Val.weekday_last());
5678+
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L}/{:L}/{:L}"), _Val.year(), _Val.month(),
5679+
_Val.weekday_last());
56655680
}
56665681

56675682
template <class _CharT, class _Traits, class _Duration>
56685683
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const hh_mm_ss<_Duration>& _Val) {
5669-
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:%T}"), _Val);
5684+
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L%T}"), _Val);
56705685
}
56715686

56725687
#pragma warning(push)
@@ -5717,7 +5732,7 @@ namespace chrono {
57175732
// clang-format on
57185733
const auto _Dp = _CHRONO floor<days>(_Val);
57195734
return _Os << _STD format(
5720-
_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{} {}"), year_month_day{_Dp}, hh_mm_ss{_Val - _Dp});
5735+
_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L} {:L}"), year_month_day{_Dp}, hh_mm_ss{_Val - _Dp});
57215736
}
57225737

57235738
template <class _CharT, class _Traits>
@@ -5727,22 +5742,22 @@ namespace chrono {
57275742

57285743
template <class _CharT, class _Traits, class _Duration>
57295744
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const utc_time<_Duration>& _Val) {
5730-
return _Os << _STD format(_STATICALLY_WIDEN(_CharT, "{:%F %T}"), _Val);
5745+
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L%F %T}"), _Val);
57315746
}
57325747

57335748
template <class _CharT, class _Traits, class _Duration>
57345749
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const tai_time<_Duration>& _Val) {
5735-
return _Os << _STD format(_STATICALLY_WIDEN(_CharT, "{:%F %T}"), _Val);
5750+
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L%F %T}"), _Val);
57365751
}
57375752

57385753
template <class _CharT, class _Traits, class _Duration>
57395754
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const gps_time<_Duration>& _Val) {
5740-
return _Os << _STD format(_STATICALLY_WIDEN(_CharT, "{:%F %T}"), _Val);
5755+
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L%F %T}"), _Val);
57415756
}
57425757

57435758
template <class _CharT, class _Traits, class _Duration>
57445759
basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const file_time<_Duration>& _Val) {
5745-
return _Os << _STD format(_STATICALLY_WIDEN(_CharT, "{:%F %T}"), _Val);
5760+
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L%F %T}"), _Val);
57465761
}
57475762

57485763
template <class _CharT, class _Traits, class _Duration>
@@ -5755,7 +5770,7 @@ namespace chrono {
57555770
basic_ostream<_CharT, _Traits>& _Os, const _Local_time_format_t<_Duration>& _Val) {
57565771
// Doesn't appear in the Standard, but allowed by N4885 [global.functions]/2.
57575772
// Implements N4885 [time.zone.zonedtime.nonmembers]/2 for zoned_time.
5758-
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:%F %T %Z}"), _Val);
5773+
return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "{:L%F %T %Z}"), _Val);
57595774
}
57605775

57615776
template <class _CharT, class _Traits, class _Duration, class _TimeZonePtr>
@@ -5899,10 +5914,10 @@ namespace chrono {
58995914
_NODISCARD auto _Write(_FormatContext& _FormatCtx, const _Ty& _Val, const tm& _Time) {
59005915
basic_ostringstream<_CharT> _Stream;
59015916

5917+
_Stream.imbue(_Specs._Localized ? _FormatCtx.locale() : locale::classic());
59025918
if (_Specs._Chrono_specs_list.empty()) {
59035919
_Stream << _Val; // N4885 [time.format]/6
59045920
} else {
5905-
_Stream.imbue(_FormatCtx.locale());
59065921
if constexpr (_Is_specialization_v<_Ty, hh_mm_ss>) {
59075922
if (_Val.is_negative()) {
59085923
_Stream << _CharT{'-'};
@@ -6262,7 +6277,7 @@ namespace chrono {
62626277
return _Fmt_str;
62636278
}
62646279

6265-
_Chrono_format_specs<_CharT> _Specs{};
6280+
_Chrono_format_specs2<_CharT> _Specs{};
62666281
basic_string_view<_CharT> _Time_zone_abbreviation{};
62676282
};
62686283
} // namespace chrono

tests/std/tests/P0355R7_calendars_and_time_zones_formatting/test.cpp

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ struct testing_callbacks {
4848
int expected_precision = -1;
4949
size_t expected_dynamic_precision = static_cast<size_t>(-1);
5050
bool expected_auto_dynamic_precision = false;
51+
bool expected_localized = false;
5152
vector<_Chrono_spec<CharT>>& expected_chrono_specs;
5253
size_t curr_index = 0;
5354

@@ -75,6 +76,9 @@ struct testing_callbacks {
7576
void _On_dynamic_precision(_Auto_id_tag) {
7677
assert(expected_auto_dynamic_precision);
7778
}
79+
void _On_localized() {
80+
assert(expected_localized);
81+
}
7882
void _On_conversion_spec(char mod, CharT type) {
7983
assert(mod == expected_chrono_specs[curr_index]._Modifier);
8084
assert(static_cast<char>(type) == expected_chrono_specs[curr_index]._Type);
@@ -900,8 +904,34 @@ void test_zoned_time_formatter() {
900904

901905
template <typename CharT>
902906
void test_locale() {
903-
assert(format(locale{"zh-CN"}, STR("{:^22%Y %B %d %A}"), 2021y / June / 16d)
907+
assert(format(locale{"zh-CN"}, STR("{:^22L%Y %B %d %A}"), 2021y / June / 16d)
904908
== STR(" 2021 \u516D\u6708 16 \u661F\u671F\u4E09 "));
909+
910+
911+
locale loc("de-DE");
912+
913+
assert(format(loc, STR("{:%S}"), 42ms) == STR("00.042"));
914+
assert(format(loc, STR("{:L%S}"), 42ms) == STR("00,042"));
915+
916+
auto stream = [=](auto value) {
917+
std::basic_ostringstream<CharT> os;
918+
os.imbue(loc);
919+
os << value;
920+
return os.str();
921+
};
922+
assert(stream(month{May}) == STR("Mai"));
923+
assert(stream(weekday{Tuesday}) == STR("Di"));
924+
assert(stream(weekday_indexed{Tuesday[3]}) == STR("Di[3]"));
925+
assert(stream(weekday_indexed{Tuesday[42]}) == STR("Di[42 is not a valid index]"));
926+
assert(stream(weekday_last{Tuesday}) == STR("Di[last]"));
927+
assert(stream(month_day{May, day{4}}) == STR("Mai/04"));
928+
assert(stream(month_day_last{May}) == STR("Mai/last"));
929+
assert(stream(month_weekday{May / Tuesday[4]}) == STR("Mai/Di[4]"));
930+
assert(stream(month_weekday_last{May / Tuesday[last]}) == STR("Mai/Di[last]"));
931+
assert(stream(year_month{2021y / May}) == STR("2021/Mai"));
932+
assert(stream(year_month_day_last{2021y / May / last}) == STR("2021/Mai/last"));
933+
assert(stream(year_month_weekday{2021y / May / Tuesday[4]}) == STR("2021/Mai/Di[4]"));
934+
assert(stream(year_month_weekday_last{2021y / May / Tuesday[last]}) == STR("2021/Mai/Di[last]"));
905935
}
906936

907937
void test() {

0 commit comments

Comments
 (0)