From 0ff513dee3de93859d36b8dc540579bdc5a8b8eb Mon Sep 17 00:00:00 2001 From: Elnar D Date: Tue, 20 Apr 2021 07:58:14 -0700 Subject: [PATCH 1/3] : Add %j support and expand %aAuw Adds %j support to all the classes that can benefit from it. This means all year_month_day* but and month_last (but only for January). Changes %aAuw to throw on invalid weekdays, but still work if the weekday is explicitly ok (so a bad year_month_day will always throw but a bad year_month_weekday can still print the weekday if that portion is ok). --- stl/inc/chrono | 73 ++++++++++++++----- .../test.cpp | 29 ++++++++ 2 files changed, 84 insertions(+), 18 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index ece4fc6268e..41163803e41 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -5485,6 +5485,7 @@ namespace chrono { unsigned int _Day = 0; unsigned int _Month = 0; int _Year = 0; + int _Yearday = 0; int _Weekday = 0; int _Hours = 0; int _Minutes = 0; @@ -5509,6 +5510,9 @@ namespace chrono { } else if constexpr (is_same_v<_Ty, month_day_last>) { _Month = static_cast(_Val.month()); _Day = static_cast(_Last_day_table[(_Month - 1) & 0xF]); + if (_Val.month() == January) { + _Yearday = 30; + } } else if constexpr (is_same_v<_Ty, month_weekday>) { _Month = static_cast(_Val.month()); _Weekday = static_cast(_Val.weekday_indexed().weekday().c_encoding()); @@ -5518,21 +5522,19 @@ namespace chrono { } else if constexpr (is_same_v<_Ty, year_month>) { _Month = static_cast(_Val.month()); _Year = static_cast(_Val.year()); - } else if constexpr (is_same_v<_Ty, year_month_day>) { - _Day = static_cast(_Val.day()); - _Month = static_cast(_Val.month()); - _Year = static_cast(_Val.year()); - _Weekday = _Val._Calculate_weekday(); - } else if constexpr (is_same_v<_Ty, year_month_day_last>) { - _Day = static_cast(_Val.day()); - _Month = static_cast(_Val.month()); - _Year = static_cast(_Val.year()); - _Weekday = year_month_day{_Val}._Calculate_weekday(); + } else if constexpr (_Is_any_of_v<_Ty, year_month_day, year_month_day_last>) { + _Day = static_cast(_Val.day()); + _Month = static_cast(_Val.month()); + _Year = static_cast(_Val.year()); + if (_Val.ok()) { + const year_month_day& _Ymd = _Val; + _Weekday = _Ymd._Calculate_weekday(); + _Yearday = (static_cast(_Val) - static_cast(_Val.year() / January / 1)).count(); + } } else if constexpr (_Is_any_of_v<_Ty, year_month_weekday, year_month_weekday_last>) { - _Day = static_cast(year_month_day{_Val}.day()); - _Month = static_cast(_Val.month()); - _Year = static_cast(_Val.year()); - _Weekday = static_cast(_Val.weekday().c_encoding()); + auto _Tm = _Fill_tm(year_month_day{_Val}); + _Tm.tm_wday = static_cast(_Val.weekday().c_encoding()); + return _Tm; } else if constexpr (_Is_specialization_v<_Ty, hh_mm_ss>) { _Hours = _Val.hours().count(); _Minutes = _Val.minutes().count(); @@ -5558,6 +5560,7 @@ namespace chrono { _Time.tm_mday = static_cast(_Day); _Time.tm_mon = static_cast(_Month) - 1; _Time.tm_year = _Year - 1900; + _Time.tm_yday = _Yearday; _Time.tm_wday = _Weekday; return _Time; } @@ -5845,16 +5848,18 @@ namespace chrono { return _Type == 'Y' || _Type == 'y' || _Type == 'C'; } else if constexpr (_Is_any_of_v<_Ty, weekday, weekday_indexed, weekday_last>) { return _Type == 'a' || _Type == 'A' || _Type == 'u' || _Type == 'w'; - } else if constexpr (_Is_any_of_v<_Ty, month_day, month_day_last>) { + } else if constexpr (is_same_v<_Ty, month_day>) { return _Is_valid_type(_Type) || _Is_valid_type(_Type); + } else if constexpr (is_same_v<_Ty, month_day_last>) { + return _Type == 'j' || _Is_valid_type(_Type); } else if constexpr (_Is_any_of_v<_Ty, month_weekday, month_weekday_last>) { return _Is_valid_type(_Type) || _Is_valid_type(_Type); } else if constexpr (is_same_v<_Ty, year_month>) { return _Is_valid_type(_Type) || _Is_valid_type(_Type); } else if constexpr (_Is_any_of_v<_Ty, year_month_day, year_month_day_last, year_month_weekday, year_month_weekday_last>) { - return _Type == 'D' || _Type == 'F' || _Is_valid_type(_Type) || _Is_valid_type(_Type) - || _Is_valid_type(_Type) || _Is_valid_type(_Type); + return _Type == 'D' || _Type == 'F' || _Type == 'j' || _Is_valid_type(_Type) + || _Is_valid_type(_Type) || _Is_valid_type(_Type) || _Is_valid_type(_Type); } else if constexpr (_Is_specialization_v<_Ty, hh_mm_ss>) { return _Type == 'H' || _Type == 'I' || _Type == 'M' || _Type == 'S' || _Type == 'r' || _Type == 'R' || _Type == 'T' || _Type == 'p'; @@ -5944,6 +5949,26 @@ namespace chrono { const auto _Month = _Time.tm_mon + 1; const bool _Has_modifier = _Spec._Modifier != '\0'; switch (_Spec._Type) { + case 'a': + case 'A': + case 'u': + case 'w': + if constexpr (_Is_any_of_v<_Ty, year_month_weekday, year_month_weekday_last>) { + if (!_Val.weekday().ok()) { + _THROW(format_error("Cannot print invalid weekday")); + } + _CharT _Fmt[3]; + _Fmt[0] = '%'; + _Fmt[1] = static_cast<_CharT>(_Spec._Type); + _Fmt[2] = '\0'; + _Os << _STD put_time(&_Time, _Fmt); + return true; + } else if constexpr (_Has_ok<_Ty>) { + if (!_Val.ok()) { + _THROW(format_error("Cannot print invalid weekday")); + } + } + return false; case 'd': case 'e': // Most months have a proper last day, but February depends on the year. @@ -5965,8 +5990,20 @@ namespace chrono { case 'j': if constexpr (_Is_specialization_v<_Ty, duration>) { _Os << _STD abs(_CHRONO duration_cast(_Val).count()); + return true; } - return true; + if constexpr (is_same_v<_Ty, month_day_last>) { + if (_Val.month() >= February) { + _THROW( + format_error("Cannot print the year day of the last day of any month other than January.")); + } + } + if constexpr (_Has_ok<_Ty>) { + if (!_Val.ok()) { + _THROW(format_error("Cannot print invalid year day")); + } + } + return false; case 'q': if constexpr (_Is_specialization_v<_Ty, duration>) { _Write_unit_suffix(_Os); 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 333c9b71c9e..c9b924e2d3c 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 @@ -437,6 +437,10 @@ void test_month_day_last_formatter() { assert(format(STR("{:%B}"), June / last) == STR("June")); assert(format(STR("{:%d}"), June / last) == STR("30")); throw_helper(STR("{:%d}"), February / last); + + assert(format(STR("{:%j}"), January / last) == STR("031")); + throw_helper(STR("{:%j}"), February / last); + throw_helper(STR("{:%j}"), April / last); } template @@ -499,6 +503,12 @@ void test_year_month_day_formatter() { assert(format(STR("{:%F %D}"), invalid) == STR("1234-00-31 00/31/34")); assert(format(STR("{:%a %A}"), year_month_day{year{1900}, month{1}, day{4}}) == STR("Thu Thursday")); assert(format(STR("{:%u %w}"), year_month_day{year{1900}, month{1}, day{4}}) == STR("4 4")); + throw_helper(STR("{:%u}"), invalid); + + assert(format(STR("{:%j}"), 1900y / January / 4) == STR("004")); + assert(format(STR("{:%j}"), 1900y / May / 7) == STR("127")); + assert(format(STR("{:%j}"), 2000y / May / 7) == STR("128")); + throw_helper(STR("{:%j}"), invalid); } template @@ -516,6 +526,13 @@ void test_year_month_day_last_formatter() { constexpr auto fmt = STR("{:%D %F, %Y %C %y, %b %B %h %m, %d %e, %a %A %u %w}"); assert(format(fmt, ymdl1) == STR("04/30/21 2021-04-30, 2021 20 21, Apr April Apr 04, 30 30, Fri Friday 5 5")); assert(format(fmt, ymdl2) == STR("02/29/04 2004-02-29, 2004 20 04, Feb February Feb 02, 29 29, Sun Sunday 7 0")); + + throw_helper(STR("{:%u}"), invalid); + + assert(format(STR("{:%j}"), 1900y / January / last) == STR("031")); + assert(format(STR("{:%j}"), 1900y / February / last) == STR("059")); + assert(format(STR("{:%j}"), 2000y / February / last) == STR("060")); + throw_helper(STR("{:%j}"), year{1900} / month{13} / last); } template @@ -539,6 +556,12 @@ void test_year_month_weekday_formatter() { constexpr auto fmt = STR("{:%D %F, %Y %C %y, %b %B %h %m, %d %e, %a %A %u %w}"); assert(format(fmt, ymwd1) == STR("04/30/21 2021-04-30, 2021 20 21, Apr April Apr 04, 30 30, Fri Friday 5 5")); assert(format(fmt, ymwd2) == STR("02/29/04 2004-02-29, 2004 20 04, Feb February Feb 02, 29 29, Sun Sunday 7 0")); + + assert(format(STR("{:%u}"), invalid1) == STR("5")); + throw_helper(STR("{:%u}"), invalid2); + + assert(format(STR("{:%j}"), 1900y / January / Tuesday[2]) == STR("009")); + throw_helper(STR("{:%j}"), invalid1); } template @@ -560,6 +583,12 @@ void test_year_month_weekday_last_formatter() { constexpr auto fmt = STR("{:%D %F, %Y %C %y, %b %B %h %m, %d %e, %a %A %u %w}"); assert(format(fmt, ymwdl1) == STR("04/30/21 2021-04-30, 2021 20 21, Apr April Apr 04, 30 30, Fri Friday 5 5")); assert(format(fmt, ymwdl2) == STR("02/29/04 2004-02-29, 2004 20 04, Feb February Feb 02, 29 29, Sun Sunday 7 0")); + + assert(format(STR("{:%u}"), invalid2) == STR("5")); + throw_helper(STR("{:%u}"), invalid1); + + assert(format(STR("{:%j}"), 1900y / January / Tuesday[last]) == STR("030")); + throw_helper(STR("{:%j}"), invalid1); } template From 445e70f9867cb1a8e0e727144875fd21a4b4c462 Mon Sep 17 00:00:00 2001 From: Elnar D Date: Tue, 20 Apr 2021 09:23:11 -0700 Subject: [PATCH 2/3] Add month_day support --- stl/inc/chrono | 23 +++++++++++-------- .../test.cpp | 6 +++++ 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index 41163803e41..1eb80f65b03 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -5507,6 +5507,11 @@ namespace chrono { } else if constexpr (is_same_v<_Ty, month_day>) { _Day = static_cast(_Val.day()); _Month = static_cast(_Val.month()); + if (_Val.month() == January) { + _Yearday = static_cast(_Day) - 1; + } else if (_Val.month() == February) { + _Yearday = 31 + static_cast(_Day) - 1; + } } else if constexpr (is_same_v<_Ty, month_day_last>) { _Month = static_cast(_Val.month()); _Day = static_cast(_Last_day_table[(_Month - 1) & 0xF]); @@ -5848,10 +5853,8 @@ namespace chrono { return _Type == 'Y' || _Type == 'y' || _Type == 'C'; } else if constexpr (_Is_any_of_v<_Ty, weekday, weekday_indexed, weekday_last>) { return _Type == 'a' || _Type == 'A' || _Type == 'u' || _Type == 'w'; - } else if constexpr (is_same_v<_Ty, month_day>) { - return _Is_valid_type(_Type) || _Is_valid_type(_Type); - } else if constexpr (is_same_v<_Ty, month_day_last>) { - return _Type == 'j' || _Is_valid_type(_Type); + } else if constexpr (_Is_any_of_v<_Ty, month_day, month_day_last>) { + return _Type == 'j' || _Is_valid_type(_Type) || _Is_valid_type(_Type); } else if constexpr (_Is_any_of_v<_Ty, month_weekday, month_weekday_last>) { return _Is_valid_type(_Type) || _Is_valid_type(_Type); } else if constexpr (is_same_v<_Ty, year_month>) { @@ -5991,16 +5994,18 @@ namespace chrono { if constexpr (_Is_specialization_v<_Ty, duration>) { _Os << _STD abs(_CHRONO duration_cast(_Val).count()); return true; - } - if constexpr (is_same_v<_Ty, month_day_last>) { + } else if constexpr (is_same_v<_Ty, month_day>) { + if (_Val.month() > February) { + _THROW(format_error("The day of year for a month_day past February is ambiguous.")); + } + } else if constexpr (is_same_v<_Ty, month_day_last>) { if (_Val.month() >= February) { - _THROW( - format_error("Cannot print the year day of the last day of any month other than January.")); + _THROW(format_error("The day of year for a month_day_last other than January is ambiguous")); } } if constexpr (_Has_ok<_Ty>) { if (!_Val.ok()) { - _THROW(format_error("Cannot print invalid year day")); + _THROW(format_error("Cannot print invalid day of year")); } } return false; 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 c9b924e2d3c..aa9310c5008 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 @@ -428,6 +428,12 @@ void test_month_day_formatter() { assert(format(STR("{:%B %d}"), June / 17) == STR("June 17")); throw_helper(STR("{:%Y}"), June / 17); + + assert(format(STR("{:%j}"), January / 5) == STR("005")); + assert(format(STR("{:%j}"), February / 5) == STR("036")); + assert(format(STR("{:%j}"), February / 28) == STR("059")); + assert(format(STR("{:%j}"), February / 29) == STR("060")); + throw_helper(STR("{:%j}"), March / 1); } template From d757a03d176c3d2c95dcb435c6bbba7f57df73fb Mon Sep 17 00:00:00 2001 From: Elnar D Date: Tue, 20 Apr 2021 12:42:18 -0700 Subject: [PATCH 3/3] Make put_time strings uniformly --- stl/inc/chrono | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index 1eb80f65b03..3ff50ed65f4 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -5918,16 +5918,7 @@ namespace chrono { } } - _CharT _Fmt_str[4]; - size_t _Next_idx = 0; - _Fmt_str[_Next_idx++] = _CharT{'%'}; - if (_Spec._Modifier != '\0') { - _Fmt_str[_Next_idx++] = static_cast<_CharT>(_Spec._Modifier); - } - _Fmt_str[_Next_idx++] = static_cast<_CharT>(_Spec._Type); - _Fmt_str[_Next_idx] = _CharT{'\0'}; - - _Stream << _STD put_time<_CharT>(&_Time, _Fmt_str); + _Stream << _STD put_time<_CharT>(&_Time, _Fmt_string(_Spec).data()); } } @@ -5960,11 +5951,7 @@ namespace chrono { if (!_Val.weekday().ok()) { _THROW(format_error("Cannot print invalid weekday")); } - _CharT _Fmt[3]; - _Fmt[0] = '%'; - _Fmt[1] = static_cast<_CharT>(_Spec._Type); - _Fmt[2] = '\0'; - _Os << _STD put_time(&_Time, _Fmt); + _Os << _STD put_time(&_Time, _Fmt_string(_Spec).data()); return true; } else if constexpr (_Has_ok<_Ty>) { if (!_Val.ok()) { @@ -6122,6 +6109,18 @@ namespace chrono { } } + _NODISCARD array<_CharT, 4> _Fmt_string(const _Chrono_spec<_CharT>& _Spec) { + array<_CharT, 4> _Fmt_str; + size_t _Next_idx = 0; + _Fmt_str[_Next_idx++] = _CharT{'%'}; + if (_Spec._Modifier != '\0') { + _Fmt_str[_Next_idx++] = static_cast<_CharT>(_Spec._Modifier); + } + _Fmt_str[_Next_idx++] = static_cast<_CharT>(_Spec._Type); + _Fmt_str[_Next_idx] = _CharT{'\0'}; + return _Fmt_str; + } + _Chrono_format_specs<_CharT> _Specs{}; bool _No_chrono_specs = false; basic_string_view<_CharT> _Time_zone_abbreviation{};