Skip to content

[libc++][chrono] Fixes year_month year wrapping. #74938

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 12, 2023

Conversation

mordante
Copy link
Member

@mordante mordante commented Dec 9, 2023

Adding months to a year_month should wrap the year when the month becomes greater than twelve or less than one.

This fixes the issue for year_month. Other classes with a year and month do not have this issue. This has been verified and tests are added to avoid possible regressions.

Also fixes some variable copy-paste errors in the tests.

Fixes #73162

Adding months to a year_month should wrap the year when the month
becomes greater than twelve or less than one.

This fixes the issue for year_month. Other classes with a year and month
do not have this issue. This has been verified and tests are added to
avoid possible regressions.

Also fixes some variable copy-paste errors in the tests.

Fixes llvm#73162
@mordante mordante requested a review from a team as a code owner December 9, 2023 16:20
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Dec 9, 2023
@llvmbot
Copy link
Member

llvmbot commented Dec 9, 2023

@llvm/pr-subscribers-libcxx

Author: Mark de Wever (mordante)

Changes

Adding months to a year_month should wrap the year when the month becomes greater than twelve or less than one.

This fixes the issue for year_month. Other classes with a year and month do not have this issue. This has been verified and tests are added to avoid possible regressions.

Also fixes some variable copy-paste errors in the tests.

Fixes #73162


Patch is 31.90 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/74938.diff

15 Files Affected:

  • (modified) libcxx/include/__chrono/year_month.h (+24-4)
  • (modified) libcxx/test/std/time/time.cal/time.cal.ym/time.cal.ym.members/plus_minus_equal_month.pass.cpp (+10)
  • (modified) libcxx/test/std/time/time.cal/time.cal.ym/time.cal.ym.nonmembers/minus.pass.cpp (+7-2)
  • (modified) libcxx/test/std/time/time.cal/time.cal.ym/time.cal.ym.nonmembers/plus.pass.cpp (+17-13)
  • (modified) libcxx/test/std/time/time.cal/time.cal.ymd/time.cal.ymd.members/plus_minus_equal_month.pass.cpp (+34)
  • (modified) libcxx/test/std/time/time.cal/time.cal.ymd/time.cal.ymd.nonmembers/plus.pass.cpp (+31-20)
  • (modified) libcxx/test/std/time/time.cal/time.cal.ymdlast/time.cal.ymdlast.members/plus_minus_equal_month.pass.cpp (+19-9)
  • (modified) libcxx/test/std/time/time.cal/time.cal.ymdlast/time.cal.ymdlast.nonmembers/minus.pass.cpp (+14-9)
  • (modified) libcxx/test/std/time/time.cal/time.cal.ymdlast/time.cal.ymdlast.nonmembers/plus.pass.cpp (+25-17)
  • (modified) libcxx/test/std/time/time.cal/time.cal.ymwd/time.cal.ymwd.members/plus_minus_equal_month.pass.cpp (+10)
  • (modified) libcxx/test/std/time/time.cal/time.cal.ymwd/time.cal.ymwd.nonmembers/minus.pass.cpp (+20-12)
  • (modified) libcxx/test/std/time/time.cal/time.cal.ymwd/time.cal.ymwd.nonmembers/plus.pass.cpp (+37-24)
  • (modified) libcxx/test/std/time/time.cal/time.cal.ymwdlast/time.cal.ymwdlast.members/plus_minus_equal_month.pass.cpp (+22-13)
  • (modified) libcxx/test/std/time/time.cal/time.cal.ymwdlast/time.cal.ymwdlast.nonmembers/minus.pass.cpp (+20-12)
  • (modified) libcxx/test/std/time/time.cal/time.cal.ymwdlast/time.cal.ymwdlast.nonmembers/plus.pass.cpp (+39-26)
diff --git a/libcxx/include/__chrono/year_month.h b/libcxx/include/__chrono/year_month.h
index f4eea8427fc51..d1657b61015b9 100644
--- a/libcxx/include/__chrono/year_month.h
+++ b/libcxx/include/__chrono/year_month.h
@@ -36,10 +36,10 @@ class year_month {
         : __y_{__yval}, __m_{__mval} {}
     _LIBCPP_HIDE_FROM_ABI inline constexpr chrono::year  year()  const noexcept { return __y_; }
     _LIBCPP_HIDE_FROM_ABI inline constexpr chrono::month month() const noexcept { return __m_; }
-    _LIBCPP_HIDE_FROM_ABI inline constexpr year_month& operator+=(const months& __dm) noexcept { this->__m_ += __dm; return *this; }
-    _LIBCPP_HIDE_FROM_ABI inline constexpr year_month& operator-=(const months& __dm) noexcept { this->__m_ -= __dm; return *this; }
-    _LIBCPP_HIDE_FROM_ABI inline constexpr year_month& operator+=(const years& __dy)  noexcept { this->__y_ += __dy; return *this; }
-    _LIBCPP_HIDE_FROM_ABI inline constexpr year_month& operator-=(const years& __dy)  noexcept { this->__y_ -= __dy; return *this; }
+    _LIBCPP_HIDE_FROM_ABI inline constexpr year_month& operator+=(const months& __dm) noexcept;
+    _LIBCPP_HIDE_FROM_ABI inline constexpr year_month& operator-=(const months& __dm) noexcept;
+    _LIBCPP_HIDE_FROM_ABI inline constexpr year_month& operator+=(const years& __dy) noexcept;
+    _LIBCPP_HIDE_FROM_ABI inline constexpr year_month& operator-=(const years& __dy) noexcept;
     _LIBCPP_HIDE_FROM_ABI inline constexpr bool ok() const noexcept { return __y_.ok() && __m_.ok(); }
 };
 
@@ -92,6 +92,26 @@ _LIBCPP_HIDE_FROM_ABI constexpr
 year_month operator-(const year_month& __lhs, const years& __rhs) noexcept
 { return __lhs + -__rhs; }
 
+_LIBCPP_HIDE_FROM_ABI inline constexpr year_month& year_month::operator+=(const months& __dm) noexcept {
+  *this = *this + __dm;
+  return *this;
+}
+
+_LIBCPP_HIDE_FROM_ABI inline constexpr year_month& year_month::operator-=(const months& __dm) noexcept {
+  *this = *this - __dm;
+  return *this;
+}
+
+_LIBCPP_HIDE_FROM_ABI inline constexpr year_month& year_month::operator+=(const years& __dy) noexcept {
+  *this = *this + __dy;
+  return *this;
+}
+
+_LIBCPP_HIDE_FROM_ABI inline constexpr year_month& year_month::operator-=(const years& __dy) noexcept {
+  *this = *this - __dy;
+  return *this;
+}
+
 } // namespace chrono
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/test/std/time/time.cal/time.cal.ym/time.cal.ym.members/plus_minus_equal_month.pass.cpp b/libcxx/test/std/time/time.cal/time.cal.ym/time.cal.ym.members/plus_minus_equal_month.pass.cpp
index 6d1273ead7391..cc9fff83a1cff 100644
--- a/libcxx/test/std/time/time.cal/time.cal.ym/time.cal.ym.members/plus_minus_equal_month.pass.cpp
+++ b/libcxx/test/std/time/time.cal/time.cal.ym/time.cal.ym.members/plus_minus_equal_month.pass.cpp
@@ -38,6 +38,16 @@ constexpr bool test() {
     assert(ym.year() == y);
   }
 
+  { // Test year wrapping
+    year_month ym{year{2020}, month{4}};
+
+    ym += months{12};
+    assert((ym == year_month{year{2021}, month{4}}));
+
+    ym -= months{12};
+    assert((ym == year_month{year{2020}, month{4}}));
+  }
+
   return true;
 }
 
diff --git a/libcxx/test/std/time/time.cal/time.cal.ym/time.cal.ym.nonmembers/minus.pass.cpp b/libcxx/test/std/time/time.cal/time.cal.ym/time.cal.ym.nonmembers/minus.pass.cpp
index 5bacee9af7f82..977c566e745ac 100644
--- a/libcxx/test/std/time/time.cal/time.cal.ym/time.cal.ym.nonmembers/minus.pass.cpp
+++ b/libcxx/test/std/time/time.cal/time.cal.ym/time.cal.ym.nonmembers/minus.pass.cpp
@@ -46,12 +46,17 @@ constexpr bool test() {
   { // year_month - months
 
     year_month ym{year{1234}, std::chrono::November};
-    for (int i = 0; i <= 10; ++i) // TODO test wrap-around
-    {
+    for (int i = 0; i <= 10; ++i) {
       year_month ym1 = ym - months{i};
       assert(static_cast<int>(ym1.year()) == 1234);
       assert(ym1.month() == month(11 - i));
     }
+    // Test the year wraps around.
+    for (int i = 12; i <= 15; ++i) {
+      year_month ym1 = ym - months{i};
+      assert(static_cast<int>(ym1.year()) == 1233);
+      assert(ym1.month() == month(11 - i + 12));
+    }
   }
 
   { // year_month - year_month
diff --git a/libcxx/test/std/time/time.cal/time.cal.ym/time.cal.ym.nonmembers/plus.pass.cpp b/libcxx/test/std/time/time.cal/time.cal.ym/time.cal.ym.nonmembers/plus.pass.cpp
index 315d74b75bbcc..60747a29714d1 100644
--- a/libcxx/test/std/time/time.cal/time.cal.ym/time.cal.ym.nonmembers/plus.pass.cpp
+++ b/libcxx/test/std/time/time.cal/time.cal.ym/time.cal.ym.nonmembers/plus.pass.cpp
@@ -29,10 +29,10 @@
 
 #include "test_macros.h"
 
-using year = std::chrono::year;
-using years = std::chrono::years;
-using month = std::chrono::month;
-using months = std::chrono::months;
+using year       = std::chrono::year;
+using years      = std::chrono::years;
+using month      = std::chrono::month;
+using months     = std::chrono::months;
 using year_month = std::chrono::year_month;
 
 // year_month + years
@@ -40,10 +40,8 @@ constexpr bool test_ym_plus_y() {
   ASSERT_NOEXCEPT(std::declval<year_month>() + std::declval<years>());
   ASSERT_NOEXCEPT(std::declval<years>() + std::declval<year_month>());
 
-  ASSERT_SAME_TYPE(
-      year_month, decltype(std::declval<year_month>() + std::declval<years>()));
-  ASSERT_SAME_TYPE(
-      year_month, decltype(std::declval<years>() + std::declval<year_month>()));
+  ASSERT_SAME_TYPE(year_month, decltype(std::declval<year_month>() + std::declval<years>()));
+  ASSERT_SAME_TYPE(year_month, decltype(std::declval<years>() + std::declval<year_month>()));
 
   year_month ym{year{1234}, std::chrono::January};
   for (int i = 0; i <= 10; ++i) {
@@ -64,10 +62,17 @@ constexpr bool test_ym_plus_m() {
   ASSERT_NOEXCEPT(std::declval<year_month>() + std::declval<months>());
   ASSERT_NOEXCEPT(std::declval<months>() + std::declval<year_month>());
 
-  ASSERT_SAME_TYPE(year_month, decltype(std::declval<year_month>() +
-                                        std::declval<months>()));
-  ASSERT_SAME_TYPE(year_month, decltype(std::declval<months>() +
-                                        std::declval<year_month>()));
+  ASSERT_SAME_TYPE(year_month, decltype(std::declval<year_month>() + std::declval<months>()));
+  ASSERT_SAME_TYPE(year_month, decltype(std::declval<months>() + std::declval<year_month>()));
+
+  {
+    // [time.cal.ym.nonmembers]/4
+    // Returns: A year_month value z such that z.ok() && z - ym == dm is true.
+    year_month ym = {year{1234}, std::chrono::January};
+    months dm     = months{42};
+    year_month z  = ym + dm;
+    assert(z.ok() && z - ym == dm);
+  }
 
   year_month ym{year{1234}, std::chrono::January};
   for (int i = 0; i <= 11; ++i) {
@@ -89,7 +94,6 @@ constexpr bool test_ym_plus_m() {
     assert(ym2.month() == month(1 + i % 12));
     assert(ym1 == ym2);
   }
-
   return true;
 }
 
diff --git a/libcxx/test/std/time/time.cal/time.cal.ymd/time.cal.ymd.members/plus_minus_equal_month.pass.cpp b/libcxx/test/std/time/time.cal/time.cal.ymd/time.cal.ymd.members/plus_minus_equal_month.pass.cpp
index f656d8f6cc056..40db399b110b8 100644
--- a/libcxx/test/std/time/time.cal/time.cal.ymd/time.cal.ymd.members/plus_minus_equal_month.pass.cpp
+++ b/libcxx/test/std/time/time.cal/time.cal.ymd/time.cal.ymd.members/plus_minus_equal_month.pass.cpp
@@ -42,6 +42,40 @@ constexpr bool test() {
     assert(static_cast<unsigned>((ymd).month()) == i + 1);
   }
 
+  { // Validate the ok status when the day is not present in the new month.
+    year_month_day ymd{year{2020}, month{3}, day{31}};
+    ymd += months{1};
+    assert((ymd == year_month_day{year{2020}, month{4}, day{31}}));
+    assert(!ymd.ok());
+
+    ymd -= months{1};
+    assert((ymd == year_month_day{year{2020}, month{3}, day{31}}));
+    assert(ymd.ok());
+  }
+
+  { // Validate the ok status when the day becomes present in the new month.
+    year_month_day ymd{year{2020}, month{4}, day{31}};
+    assert(!ymd.ok());
+
+    ymd += months{1};
+    assert((ymd == year_month_day{year{2020}, month{5}, day{31}}));
+    assert(ymd.ok());
+
+    ymd -= months{2};
+    assert((ymd == year_month_day{year{2020}, month{3}, day{31}}));
+    assert(ymd.ok());
+  }
+
+  { // Test year wrapping
+    year_month_day ymd{year{2020}, month{4}, day{31}};
+
+    ymd += months{12};
+    assert((ymd == year_month_day{year{2021}, month{4}, day{31}}));
+
+    ymd -= months{12};
+    assert((ymd == year_month_day{year{2020}, month{4}, day{31}}));
+  }
+
   return true;
 }
 
diff --git a/libcxx/test/std/time/time.cal/time.cal.ymd/time.cal.ymd.nonmembers/plus.pass.cpp b/libcxx/test/std/time/time.cal/time.cal.ymd/time.cal.ymd.nonmembers/plus.pass.cpp
index 27626e40fceb7..b172cb1384570 100644
--- a/libcxx/test/std/time/time.cal/time.cal.ymd/time.cal.ymd.nonmembers/plus.pass.cpp
+++ b/libcxx/test/std/time/time.cal/time.cal.ymd/time.cal.ymd.nonmembers/plus.pass.cpp
@@ -39,32 +39,43 @@ using year_month_day = std::chrono::year_month_day;
 constexpr bool test() {
   { // year_month_day + months
     year_month_day ym{year{1234}, std::chrono::January, day{12}};
-    for (int i = 0; i <= 10; ++i) // TODO test wrap-around
-    {
-      year_month_day ym1 = ym + months{i};
-      year_month_day ym2 = months{i} + ym;
-      assert(static_cast<int>(ym1.year()) == 1234);
-      assert(static_cast<int>(ym2.year()) == 1234);
-      assert(ym1.month() == month(1 + i));
-      assert(ym2.month() == month(1 + i));
-      assert(ym1.day() == day{12});
-      assert(ym2.day() == day{12});
-      assert(ym1 == ym2);
+    for (int i = 0; i <= 10; ++i) {
+      year_month_day ymd1 = ym + months{i};
+      year_month_day ymd2 = months{i} + ym;
+      assert(static_cast<int>(ymd1.year()) == 1234);
+      assert(static_cast<int>(ymd2.year()) == 1234);
+      assert(ymd1.month() == month(1 + i));
+      assert(ymd2.month() == month(1 + i));
+      assert(ymd1.day() == day{12});
+      assert(ymd2.day() == day{12});
+      assert(ymd1 == ymd2);
+    }
+    // Test the year wraps around.
+    for (int i = 12; i <= 15; ++i) {
+      year_month_day ymd1 = ym + months{i};
+      year_month_day ymd2 = months{i} + ym;
+      assert(static_cast<int>(ymd1.year()) == 1235);
+      assert(static_cast<int>(ymd2.year()) == 1235);
+      assert(ymd1.month() == month(1 + i - 12));
+      assert(ymd2.month() == month(1 + i - 12));
+      assert(ymd1.day() == day{12});
+      assert(ymd2.day() == day{12});
+      assert(ymd1 == ymd2);
     }
   }
 
   { // year_month_day + years
     year_month_day ym{year{1234}, std::chrono::January, day{12}};
     for (int i = 0; i <= 10; ++i) {
-      year_month_day ym1 = ym + years{i};
-      year_month_day ym2 = years{i} + ym;
-      assert(static_cast<int>(ym1.year()) == i + 1234);
-      assert(static_cast<int>(ym2.year()) == i + 1234);
-      assert(ym1.month() == std::chrono::January);
-      assert(ym2.month() == std::chrono::January);
-      assert(ym1.day() == day{12});
-      assert(ym2.day() == day{12});
-      assert(ym1 == ym2);
+      year_month_day ymd1 = ym + years{i};
+      year_month_day ymd2 = years{i} + ym;
+      assert(static_cast<int>(ymd1.year()) == i + 1234);
+      assert(static_cast<int>(ymd2.year()) == i + 1234);
+      assert(ymd1.month() == std::chrono::January);
+      assert(ymd2.month() == std::chrono::January);
+      assert(ymd1.day() == day{12});
+      assert(ymd2.day() == day{12});
+      assert(ymd1 == ymd2);
     }
   }
 
diff --git a/libcxx/test/std/time/time.cal/time.cal.ymdlast/time.cal.ymdlast.members/plus_minus_equal_month.pass.cpp b/libcxx/test/std/time/time.cal/time.cal.ymdlast/time.cal.ymdlast.members/plus_minus_equal_month.pass.cpp
index dfa0f002bfb92..1c08479596016 100644
--- a/libcxx/test/std/time/time.cal/time.cal.ymdlast/time.cal.ymdlast.members/plus_minus_equal_month.pass.cpp
+++ b/libcxx/test/std/time/time.cal/time.cal.ymdlast/time.cal.ymdlast.members/plus_minus_equal_month.pass.cpp
@@ -29,15 +29,25 @@ constexpr bool test() {
   for (unsigned i = 0; i <= 10; ++i) {
     year y{1234};
     month_day_last mdl{month{i}};
-    year_month_day_last ym(y, mdl);
-    assert(static_cast<unsigned>((ym += months{2}).month()) == i + 2);
-    assert(ym.year() == y);
-    assert(static_cast<unsigned>((ym).month()) == i + 2);
-    assert(ym.year() == y);
-    assert(static_cast<unsigned>((ym -= months{1}).month()) == i + 1);
-    assert(ym.year() == y);
-    assert(static_cast<unsigned>((ym).month()) == i + 1);
-    assert(ym.year() == y);
+    year_month_day_last ymdl(y, mdl);
+    assert(static_cast<unsigned>((ymdl += months{2}).month()) == i + 2);
+    assert(ymdl.year() == y);
+    assert(static_cast<unsigned>((ymdl).month()) == i + 2);
+    assert(ymdl.year() == y);
+    assert(static_cast<unsigned>((ymdl -= months{1}).month()) == i + 1);
+    assert(ymdl.year() == y);
+    assert(static_cast<unsigned>((ymdl).month()) == i + 1);
+    assert(ymdl.year() == y);
+  }
+
+  { // Test year wrapping
+    year_month_day_last ymdl{year{2020}, month_day_last{month{4}}};
+
+    ymdl += months{12};
+    assert((ymdl == year_month_day_last{year{2021}, month_day_last{month{4}}}));
+
+    ymdl -= months{12};
+    assert((ymdl == year_month_day_last{year{2020}, month_day_last{month{4}}}));
   }
 
   return true;
diff --git a/libcxx/test/std/time/time.cal/time.cal.ymdlast/time.cal.ymdlast.nonmembers/minus.pass.cpp b/libcxx/test/std/time/time.cal/time.cal.ymdlast/time.cal.ymdlast.nonmembers/minus.pass.cpp
index 009790a450e9d..22d1acfbe2827 100644
--- a/libcxx/test/std/time/time.cal/time.cal.ymdlast/time.cal.ymdlast.nonmembers/minus.pass.cpp
+++ b/libcxx/test/std/time/time.cal/time.cal.ymdlast/time.cal.ymdlast.nonmembers/minus.pass.cpp
@@ -38,22 +38,27 @@ constexpr bool test() {
 
   { // year_month_day_last - years
 
-    year_month_day_last ym{year{1234}, month_day_last{December}};
+    year_month_day_last ymdl{year{1234}, month_day_last{December}};
     for (int i = 0; i <= 10; ++i) {
-      year_month_day_last ym1 = ym - years{i};
-      assert(static_cast<int>(ym1.year()) == 1234 - i);
-      assert(ym1.month() == December);
+      year_month_day_last ymdl1 = ymdl - years{i};
+      assert(static_cast<int>(ymdl1.year()) == 1234 - i);
+      assert(ymdl1.month() == December);
     }
   }
 
   { // year_month_day_last - months
 
-    // TODO test wrapping
-    year_month_day_last ym{year{1234}, month_day_last{December}};
+    year_month_day_last ymdl{year{1234}, month_day_last{December}};
     for (unsigned i = 0; i <= 10; ++i) {
-      year_month_day_last ym1 = ym - months{i};
-      assert(static_cast<int>(ym1.year()) == 1234);
-      assert(static_cast<unsigned>(ym1.month()) == 12U - i);
+      year_month_day_last ymdl1 = ymdl - months{i};
+      assert(static_cast<int>(ymdl1.year()) == 1234);
+      assert(static_cast<unsigned>(ymdl1.month()) == 12U - i);
+    }
+    // Test the year wraps around.
+    for (unsigned i = 12; i <= 15; ++i) {
+      year_month_day_last ymdl1 = ymdl - months{i};
+      assert(static_cast<int>(ymdl1.year()) == 1233);
+      assert(static_cast<unsigned>(ymdl1.month()) == 12U - i + 12);
     }
   }
 
diff --git a/libcxx/test/std/time/time.cal/time.cal.ymdlast/time.cal.ymdlast.nonmembers/plus.pass.cpp b/libcxx/test/std/time/time.cal/time.cal.ymdlast/time.cal.ymdlast.nonmembers/plus.pass.cpp
index 4cbc3d1a1994e..1a4609e761baa 100644
--- a/libcxx/test/std/time/time.cal/time.cal.ymdlast/time.cal.ymdlast.nonmembers/plus.pass.cpp
+++ b/libcxx/test/std/time/time.cal/time.cal.ymdlast/time.cal.ymdlast.nonmembers/plus.pass.cpp
@@ -49,29 +49,37 @@ constexpr bool test() {
 
   { // year_month_day_last + months
     year_month_day_last ym{year{1234}, month_day_last{January}};
-    for (int i = 0; i <= 10; ++i) // TODO test wrap-around
-    {
-      year_month_day_last ym1 = ym + months{i};
-      year_month_day_last ym2 = months{i} + ym;
-      assert(static_cast<int>(ym1.year()) == 1234);
-      assert(static_cast<int>(ym2.year()) == 1234);
-      assert(ym1.month() == month(1 + i));
-      assert(ym2.month() == month(1 + i));
-      assert(ym1 == ym2);
+    for (int i = 0; i <= 10; ++i) {
+      year_month_day_last ymdl1 = ym + months{i};
+      year_month_day_last ymdl2 = months{i} + ym;
+      assert(static_cast<int>(ymdl1.year()) == 1234);
+      assert(static_cast<int>(ymdl2.year()) == 1234);
+      assert(ymdl1.month() == month(1 + i));
+      assert(ymdl2.month() == month(1 + i));
+      assert(ymdl1 == ymdl2);
+    }
+    // Test the year wraps around.
+    for (int i = 12; i <= 15; ++i) {
+      year_month_day_last ymdl1 = ym + months{i};
+      year_month_day_last ymdl2 = months{i} + ym;
+      assert(static_cast<int>(ymdl1.year()) == 1235);
+      assert(static_cast<int>(ymdl2.year()) == 1235);
+      assert(ymdl1.month() == month(1 + i - 12));
+      assert(ymdl2.month() == month(1 + i - 12));
+      assert(ymdl1 == ymdl2);
     }
   }
 
   { // year_month_day_last + years
-
     year_month_day_last ym{year{1234}, month_day_last{January}};
     for (int i = 0; i <= 10; ++i) {
-      year_month_day_last ym1 = ym + years{i};
-      year_month_day_last ym2 = years{i} + ym;
-      assert(static_cast<int>(ym1.year()) == i + 1234);
-      assert(static_cast<int>(ym2.year()) == i + 1234);
-      assert(ym1.month() == std::chrono::January);
-      assert(ym2.month() == std::chrono::January);
-      assert(ym1 == ym2);
+      year_month_day_last ymdl1 = ym + years{i};
+      year_month_day_last ymdl2 = years{i} + ym;
+      assert(static_cast<int>(ymdl1.year()) == i + 1234);
+      assert(static_cast<int>(ymdl2.year()) == i + 1234);
+      assert(ymdl1.month() == std::chrono::January);
+      assert(ymdl2.month() == std::chrono::January);
+      assert(ymdl1 == ymdl2);
     }
   }
 
diff --git a/libcxx/test/std/time/time.cal/time.cal.ymwd/time.cal.ymwd.members/plus_minus_equal_month.pass.cpp b/libcxx/test/std/time/time.cal/time.cal.ymwd/time.cal.ymwd.members/plus_minus_equal_month.pass.cpp
index 8b49540be3fa2..6aaab2dbfb31d 100644
--- a/libcxx/test/std/time/time.cal/time.cal.ymwd/time.cal.ymwd.members/plus_minus_equal_month.pass.cpp
+++ b/libcxx/test/std/time/time.cal/time.cal.ymwd/time.cal.ymwd.members/plus_minus_equal_month.pass.cpp
@@ -54,6 +54,16 @@ constexpr bool test() {
     assert(ymwd.index() == 2);
   }
 
+  { // Test year wrapping
+    year_month_weekday ymwd{year{2020}, month{4}, weekday_indexed{Tuesday, 2}};
+
+    ymwd += months{12};
+    assert((ymwd == year_month_weekday{year{2021}, month{4}, weekday_indexed{Tuesday, 2}}));
+
+    ymwd -= months{12};
+    assert((ymwd == year_month_weekday{year{2020}, month{4}, weekday_indexed{Tuesday, 2}}));
+  }
+
   return true;
 }
 
diff --git a/libcxx/test/std/time/time.cal/time.cal.ymwd/time.cal.ymwd.nonmembers/minus.pass.cpp b/libcxx/test/std/time/time.cal/time.cal.ymwd/time.cal.ymwd.nonmembers/minus.pass.cpp
index 2ab3462d7f7d1..5546d34ca22dd 100644
--- a/libcxx/test/std/time/time.cal/time.cal.ymwd/time.cal.ymwd.nonmembers/minus.pass.cpp
+++ b/libcxx/test/std/time/time.cal/time.cal.ymwd/time.cal.ymwd.nonmembers/minus.pass.cpp
@@ -35,24 +35,32 @@ constexpr bool test() {
   constexpr weekday Tuesday = std::chrono::Tuesday;
 
   { // year_month_weekday - years
-    year_month_weekday ym{year{1234}, November, weekday_indexed{Tuesday, 1}};
+    year_month_weekday ymwd{year{1234}, November, weekday_indexed{Tuesday, 1}};
     for (int i = 0; i <= 10; ++i) {
-      year_month_weekday ym1 = ym - years{i};
-      assert(static_cast<int>(ym1.year()) == 1234 - i);
-      assert(ym1.month() == November);
-      assert(ym1.weekday() == Tuesday);
-      assert(ym1.index() == 1);
+      year_month_weekday ymwd1 = ymwd - years{i};
+      assert(static_cast<int>(ymwd1.year()) == 1234 - i);
+      assert(ymwd1.month() == November);
+      assert(ymwd1.weekday() == Tuesday);
+      assert(ymwd1.index() == 1);
     }
   }
 
   { // year_month_weekday - months
-    year_month_weekday ym{year{1234}, November, weekday_indexed{Tuesday, 2}};
+    year_month_weekday ymwd{year{1234}, November, weekday_indexed{Tuesday, 2}};
     for (unsigned i = 1; i <= 10; ++i) {
-      year_month_weekday ym1 = ym - months{i};
-      assert(ym1.year() == year{1234});
-      assert(ym1.month() == month{11 - i});
-      assert(ym1.weekday() == Tuesday);
-      assert(ym1.index() == 2);
+      year_month_weekday ymwd1 = ymwd - months{i};
+      assert(ymwd1.year() == year{1234});
+      assert(ymwd1.month() == month{11 - i});
+      assert(ymwd1.weekday() == Tuesday);
+      assert(ymwd1.index(...
[truncated]

@mordante mordante merged commit 766bf14 into llvm:main Dec 12, 2023
@mordante mordante deleted the GH-fixes_chrono_arithmetic branch December 12, 2023 18:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[libc++] std::chrono::year_month::operator+=(month) is broken
3 participants