diff --git a/stl/inc/chrono b/stl/inc/chrono index e0f27990fc9..6c82f5177d5 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -3211,11 +3211,23 @@ namespace chrono { } _NODISCARD static pair _Decompose_year(const int _Year) { - int _Two_d_year = _Year % 100; - if (_Two_d_year < 0) { - _Two_d_year += 100; + // LWG-3831: the two-digit year is taken without regard to the sign of the whole year number. + pair _Result(_Year, _Year % 100); + _Result.first -= _Result.second; + if (_Result.second < 0) { + _Result.first -= 100; + _Result.second = -_Result.second; + } + + return _Result; + } + + _NODISCARD static int _Compose_year(const int _Century, const int _Two_d_year) { + if (_Century >= 0 || _Two_d_year == 0) { + return _Century + _Two_d_year; + } else { + return _Century + (100 - _Two_d_year); } - return {_Year - _Two_d_year, _Two_d_year}; } _NODISCARD bool _Update_if_valid(const tm& _Tp, const bool _Full_year) { @@ -3354,7 +3366,7 @@ namespace chrono { bool _Have_year = false; int _Year{0}; if (_Two_dig_year) { - _Year = *_Century + *_Two_dig_year; + _Year = _Compose_year(*_Century, *_Two_dig_year); _Have_year = true; if (_Day_of_year) { _No_err = _No_err && _Yday_to_month_day(*_Day_of_year, _Year); @@ -3450,7 +3462,7 @@ namespace chrono { // %C is only combined with %g if %G is missing, to avoid an unnecessary parse failure when the ISO and // Gregorian years are in different centuries. if (_Two_dig_iso_year && _Century && !_Iso_year) { - _No_err = _No_err && _Update(_Iso_year, *_Century + *_Two_dig_iso_year); + _No_err = _No_err && _Update(_Iso_year, _Compose_year(*_Century, *_Two_dig_iso_year)); } return _No_err; @@ -3588,7 +3600,7 @@ namespace chrono { if constexpr (_Can_rep_day) { if (_For_time_point) { - const year_month_day _Ymd{year{*_Century + *_Two_dig_year}, + const year_month_day _Ymd{year{_Compose_year(*_Century, *_Two_dig_year)}, month{static_cast(*_Month)}, day{static_cast(*_Day)}}; _Result += _CHRONO duration_cast<_DurationType>(static_cast(_Ymd).time_since_epoch()); } else if (_Used & _F_doy) { @@ -3724,7 +3736,7 @@ namespace chrono { _NODISCARD bool _Make_year(year& _Year_result) { if (_Calculate_year_fields() && _Used_fields() == _F_year) { - _Year_result = year{*_Century + *_Two_dig_year}; + _Year_result = year{_Compose_year(*_Century, *_Two_dig_year)}; return true; } else { return false; @@ -3733,8 +3745,8 @@ namespace chrono { _NODISCARD bool _Make_year_month(year_month& _Year_month_result) { if (_Calculate_year_fields() && _Used_fields() == (_F_mon | _F_year)) { - _Year_month_result = - year_month{year{*_Century + *_Two_dig_year}, month{static_cast(*_Month)}}; + _Year_month_result = year_month{ + year{_Compose_year(*_Century, *_Two_dig_year)}, month{static_cast(*_Month)}}; return true; } else { return false; @@ -3752,7 +3764,7 @@ namespace chrono { } if (_Consistent && _Test_bits(_Used_fields(), _Required, _Optional)) { - _Year_month_day_result = year_month_day{year{*_Century + *_Two_dig_year}, + _Year_month_day_result = year_month_day{year{_Compose_year(*_Century, *_Two_dig_year)}, month{static_cast(*_Month)}, day{static_cast(*_Day)}}; return true; } else { diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index bd9e318b585..c3951340e80 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -517,7 +517,6 @@ std/language.support/cmp/cmp.alg/weak_order.pass.cpp FAIL # This test also has a bogus assumption about the file_time epoch, and file_time is doomed on Windows. std/time/time.clock/time.clock.file/ostream.pass.cpp FAIL -# GH-4248: : format() %y mishandles negative year values # LLVM-74727: [libc++] Should formatting year{-99} with %C produce "-1" or "-01"? std/time/time.syn/formatter.year.pass.cpp:2 FAIL 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 014b72f9477..cb1394236fb 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 @@ -470,7 +470,7 @@ void test_year_formatter() { empty_braces_helper(year{121}, STR("0121")); assert(format(STR("{:%Y %y%C}"), year{1912}) == STR("1912 1219")); - assert(format(STR("{:%Y %y%C}"), year{-1912}) == STR("-1912 88-20")); + assert(format(STR("{:%Y %y%C}"), year{-1912}) == STR("-1912 12-20")); assert(format(STR("{:%Y %y%C}"), year{-200}) == STR("-0200 00-02")); assert(format(STR("{:%Y %y%C}"), year{200}) == STR("0200 0002")); // TRANSITION, add tests for EY Oy Ey EC @@ -478,6 +478,9 @@ void test_year_formatter() { empty_braces_helper(year{1900}, STR("1900")); empty_braces_helper(year{2000}, STR("2000")); empty_braces_helper(year{-32768}, STR("-32768 is not a valid year")); + + // [time.format] example + assert(format(STR("{:%C %y}"), year{-1976}) == STR("-20 76")); } template diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_io/test.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_io/test.cpp index 72c50526682..8ee6020e17e 100644 --- a/tests/std/tests/P0355R7_calendars_and_time_zones_io/test.cpp +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_io/test.cpp @@ -541,8 +541,14 @@ void parse_calendar_types_basic() { assert(y == 1999y); // negative century - test_parse("-1 5", "%C %y", y); + test_parse("-1 95", "%C %y", y); assert(y == -95y); + test_parse("-2 00", "%C %y", y); + assert(y == -200y); + + // [time.parse] example + test_parse("-20 76", "%3C %y", y); + assert(y == -1976y); // check consistency, or lack thereof test_parse("1887 18 87", "%Y %C %y", y);