diff --git a/stl/inc/chrono b/stl/inc/chrono index ece4fc6268e..3ff50ed65f4 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; @@ -5506,9 +5507,17 @@ 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]); + 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 +5527,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 +5565,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; } @@ -5846,15 +5854,15 @@ namespace chrono { } 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>) { - return _Is_valid_type(_Type) || _Is_valid_type(_Type); + 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>) { 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'; @@ -5910,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()); } } @@ -5944,6 +5943,22 @@ 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")); + } + _Os << _STD put_time(&_Time, _Fmt_string(_Spec).data()); + 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 +5980,22 @@ namespace chrono { case 'j': if constexpr (_Is_specialization_v<_Ty, duration>) { _Os << _STD abs(_CHRONO duration_cast(_Val).count()); + return true; + } 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("The day of year for a month_day_last other than January is ambiguous")); + } } - return true; + if constexpr (_Has_ok<_Ty>) { + if (!_Val.ok()) { + _THROW(format_error("Cannot print invalid day of year")); + } + } + return false; case 'q': if constexpr (_Is_specialization_v<_Ty, duration>) { _Write_unit_suffix(_Os); @@ -6080,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{}; 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..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 @@ -437,6 +443,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 +509,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 +532,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 +562,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 +589,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