[libcxx-commits] [libcxx] [libc++][chrono] Fixes year_month year wrapping. (PR #74938)

Mark de Wever via libcxx-commits libcxx-commits at lists.llvm.org
Sat Dec 9 08:20:27 PST 2023


https://github.com/mordante created https://github.com/llvm/llvm-project/pull/74938

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 https://github.com/llvm/llvm-project/issues/73162

>From 00ef9cd4eaf9328ace6e5d4c3dbf5cc39cb2479b Mon Sep 17 00:00:00 2001
From: Mark de Wever <koraq at xs4all.nl>
Date: Fri, 24 Nov 2023 08:23:01 +0100
Subject: [PATCH] [libc++][chrono] Fixes year_month year wrapping.

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 https://github.com/llvm/llvm-project/issues/73162
---
 libcxx/include/__chrono/year_month.h          | 28 ++++++--
 .../plus_minus_equal_month.pass.cpp           | 10 +++
 .../time.cal.ym.nonmembers/minus.pass.cpp     |  9 ++-
 .../time.cal.ym.nonmembers/plus.pass.cpp      | 30 +++++----
 .../plus_minus_equal_month.pass.cpp           | 34 ++++++++++
 .../time.cal.ymd.nonmembers/plus.pass.cpp     | 51 +++++++++------
 .../plus_minus_equal_month.pass.cpp           | 28 +++++---
 .../minus.pass.cpp                            | 23 ++++---
 .../time.cal.ymdlast.nonmembers/plus.pass.cpp | 42 +++++++-----
 .../plus_minus_equal_month.pass.cpp           | 10 +++
 .../time.cal.ymwd.nonmembers/minus.pass.cpp   | 32 +++++----
 .../time.cal.ymwd.nonmembers/plus.pass.cpp    | 61 ++++++++++-------
 .../plus_minus_equal_month.pass.cpp           | 35 ++++++----
 .../minus.pass.cpp                            | 32 +++++----
 .../plus.pass.cpp                             | 65 +++++++++++--------
 15 files changed, 329 insertions(+), 161 deletions(-)

diff --git a/libcxx/include/__chrono/year_month.h b/libcxx/include/__chrono/year_month.h
index f4eea8427fc512..d1657b61015b9f 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 6d1273ead73919..cc9fff83a1cff1 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 5bacee9af7f821..977c566e745ac1 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 315d74b75bbcc1..60747a29714d11 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 f656d8f6cc056c..40db399b110b83 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 27626e40fceb7f..b172cb13845704 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 dfa0f002bfb925..1c084795960162 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 009790a450e9da..22d1acfbe28278 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 4cbc3d1a1994e6..1a4609e761baa7 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 8b49540be3fa2b..6aaab2dbfb31d3 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 2ab3462d7f7d18..5546d34ca22dd0 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() == 2);
+    }
+    // Test the year wraps around.
+    for (unsigned i = 12; i <= 15; ++i) {
+      year_month_weekday ymwd1 = ymwd - months{i};
+      assert(ymwd1.year() == year{1233});
+      assert(ymwd1.month() == month{11 - i + 12});
+      assert(ymwd1.weekday() == Tuesday);
+      assert(ymwd1.index() == 2);
     }
   }
 
diff --git a/libcxx/test/std/time/time.cal/time.cal.ymwd/time.cal.ymwd.nonmembers/plus.pass.cpp b/libcxx/test/std/time/time.cal/time.cal.ymwd/time.cal.ymwd.nonmembers/plus.pass.cpp
index e757d81db4a149..1601e8fecb7a4b 100644
--- a/libcxx/test/std/time/time.cal/time.cal.ymwd/time.cal.ymwd.nonmembers/plus.pass.cpp
+++ b/libcxx/test/std/time/time.cal/time.cal.ymwd/time.cal.ymwd.nonmembers/plus.pass.cpp
@@ -43,36 +43,49 @@ constexpr bool test() {
 
   { // year_month_weekday + months (and switched)
     year_month_weekday ym{year{1234}, January, weekday_indexed{Tuesday, 3}};
-    for (int i = 0; i <= 10; ++i) // TODO test wrap-around
-    {
-      year_month_weekday ym1 = ym + months{i};
-      year_month_weekday 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.weekday() == Tuesday);
-      assert(ym2.weekday() == Tuesday);
-      assert(ym1.index() == 3);
-      assert(ym2.index() == 3);
-      assert(ym1 == ym2);
+    for (int i = 0; i <= 10; ++i) {
+      year_month_weekday ymwd1 = ym + months{i};
+      year_month_weekday ymwd2 = months{i} + ym;
+      assert(static_cast<int>(ymwd1.year()) == 1234);
+      assert(static_cast<int>(ymwd2.year()) == 1234);
+      assert(ymwd1.month() == month(1 + i));
+      assert(ymwd2.month() == month(1 + i));
+      assert(ymwd1.weekday() == Tuesday);
+      assert(ymwd2.weekday() == Tuesday);
+      assert(ymwd1.index() == 3);
+      assert(ymwd2.index() == 3);
+      assert(ymwd1 == ymwd2);
+    }
+    // Test the year wraps around.
+    for (int i = 12; i <= 15; ++i) {
+      year_month_weekday ymwd1 = ym + months{i};
+      year_month_weekday ymwd2 = months{i} + ym;
+      assert(static_cast<int>(ymwd1.year()) == 1235);
+      assert(static_cast<int>(ymwd2.year()) == 1235);
+      assert(ymwd1.month() == month(1 + i - 12));
+      assert(ymwd2.month() == month(1 + i - 12));
+      assert(ymwd1.weekday() == Tuesday);
+      assert(ymwd2.weekday() == Tuesday);
+      assert(ymwd1.index() == 3);
+      assert(ymwd2.index() == 3);
+      assert(ymwd1 == ymwd2);
     }
   }
 
   { // year_month_weekday + years (and switched)
     year_month_weekday ym{year{1234}, std::chrono::January, weekday_indexed{Tuesday, 3}};
     for (int i = 0; i <= 10; ++i) {
-      year_month_weekday ym1 = ym + years{i};
-      year_month_weekday ym2 = years{i} + ym;
-      assert(static_cast<int>(ym1.year()) == i + 1234);
-      assert(static_cast<int>(ym2.year()) == i + 1234);
-      assert(ym1.month() == January);
-      assert(ym2.month() == January);
-      assert(ym1.weekday() == Tuesday);
-      assert(ym2.weekday() == Tuesday);
-      assert(ym1.index() == 3);
-      assert(ym2.index() == 3);
-      assert(ym1 == ym2);
+      year_month_weekday ymwd1 = ym + years{i};
+      year_month_weekday ymwd2 = years{i} + ym;
+      assert(static_cast<int>(ymwd1.year()) == i + 1234);
+      assert(static_cast<int>(ymwd2.year()) == i + 1234);
+      assert(ymwd1.month() == January);
+      assert(ymwd2.month() == January);
+      assert(ymwd1.weekday() == Tuesday);
+      assert(ymwd2.weekday() == Tuesday);
+      assert(ymwd1.index() == 3);
+      assert(ymwd2.index() == 3);
+      assert(ymwd1 == ymwd2);
     }
   }
 
diff --git a/libcxx/test/std/time/time.cal/time.cal.ymwdlast/time.cal.ymwdlast.members/plus_minus_equal_month.pass.cpp b/libcxx/test/std/time/time.cal/time.cal.ymwdlast/time.cal.ymwdlast.members/plus_minus_equal_month.pass.cpp
index 10c7902cf2f494..ff52b1466cefa5 100644
--- a/libcxx/test/std/time/time.cal/time.cal.ymwdlast/time.cal.ymwdlast.members/plus_minus_equal_month.pass.cpp
+++ b/libcxx/test/std/time/time.cal/time.cal.ymwdlast/time.cal.ymwdlast.members/plus_minus_equal_month.pass.cpp
@@ -31,25 +31,34 @@ constexpr bool test() {
 
   for (unsigned i = 0; i <= 10; ++i) {
     year y{1234};
-    year_month_weekday_last ymwd(y, month{i}, weekday_last{Tuesday});
+    year_month_weekday_last ymwdl(y, month{i}, weekday_last{Tuesday});
 
-    assert(static_cast<unsigned>((ymwd += months{2}).month()) == i + 2);
-    assert(ymwd.year() == y);
-    assert(ymwd.weekday() == Tuesday);
+    assert(static_cast<unsigned>((ymwdl += months{2}).month()) == i + 2);
+    assert(ymwdl.year() == y);
+    assert(ymwdl.weekday() == Tuesday);
 
-    assert(static_cast<unsigned>((ymwd).month()) == i + 2);
-    assert(ymwd.year() == y);
-    assert(ymwd.weekday() == Tuesday);
+    assert(static_cast<unsigned>((ymwdl).month()) == i + 2);
+    assert(ymwdl.year() == y);
+    assert(ymwdl.weekday() == Tuesday);
 
-    assert(static_cast<unsigned>((ymwd -= months{1}).month()) == i + 1);
-    assert(ymwd.year() == y);
-    assert(ymwd.weekday() == Tuesday);
+    assert(static_cast<unsigned>((ymwdl -= months{1}).month()) == i + 1);
+    assert(ymwdl.year() == y);
+    assert(ymwdl.weekday() == Tuesday);
 
-    assert(static_cast<unsigned>((ymwd).month()) == i + 1);
-    assert(ymwd.year() == y);
-    assert(ymwd.weekday() == Tuesday);
+    assert(static_cast<unsigned>((ymwdl).month()) == i + 1);
+    assert(ymwdl.year() == y);
+    assert(ymwdl.weekday() == Tuesday);
   }
 
+  { // Test year wrapping
+    year_month_weekday_last ymwdl{year{2020}, month{4}, weekday_last{Tuesday}};
+
+    ymwdl += months{12};
+    assert((ymwdl == year_month_weekday_last{year{2021}, month{4}, weekday_last{Tuesday}}));
+
+    ymwdl -= months{12};
+    assert((ymwdl == year_month_weekday_last{year{2020}, month{4}, weekday_last{Tuesday}}));
+  }
   return true;
 }
 
diff --git a/libcxx/test/std/time/time.cal/time.cal.ymwdlast/time.cal.ymwdlast.nonmembers/minus.pass.cpp b/libcxx/test/std/time/time.cal/time.cal.ymwdlast/time.cal.ymwdlast.nonmembers/minus.pass.cpp
index a0dc7545dc8a5b..6fe2ec3b6606d1 100644
--- a/libcxx/test/std/time/time.cal/time.cal.ymwdlast/time.cal.ymwdlast.nonmembers/minus.pass.cpp
+++ b/libcxx/test/std/time/time.cal/time.cal.ymwdlast/time.cal.ymwdlast.nonmembers/minus.pass.cpp
@@ -35,24 +35,32 @@ constexpr bool test() {
   constexpr weekday Tuesday = std::chrono::Tuesday;
 
   { // year_month_weekday_last - years
-    year_month_weekday_last ym{year{1234}, October, weekday_last{Tuesday}};
+    year_month_weekday_last ymwdl{year{1234}, October, weekday_last{Tuesday}};
     for (int i = 0; i <= 10; ++i) {
-      year_month_weekday_last ym1 = ym - years{i};
-      assert(ym1.year() == year{1234 - i});
-      assert(ym1.month() == October);
-      assert(ym1.weekday() == Tuesday);
-      assert(ym1.weekday_last() == weekday_last{Tuesday});
+      year_month_weekday_last ymwdl1 = ymwdl - years{i};
+      assert(ymwdl1.year() == year{1234 - i});
+      assert(ymwdl1.month() == October);
+      assert(ymwdl1.weekday() == Tuesday);
+      assert(ymwdl1.weekday_last() == weekday_last{Tuesday});
     }
   }
 
   { // year_month_weekday_last - months
-    year_month_weekday_last ym{year{1234}, October, weekday_last{Tuesday}};
+    year_month_weekday_last ymwdl{year{1234}, October, weekday_last{Tuesday}};
     for (unsigned i = 0; i < 10; ++i) {
-      year_month_weekday_last ym1 = ym - months{i};
-      assert(ym1.year() == year{1234});
-      assert(ym1.month() == month{10 - i});
-      assert(ym1.weekday() == Tuesday);
-      assert(ym1.weekday_last() == weekday_last{Tuesday});
+      year_month_weekday_last ymwdl1 = ymwdl - months{i};
+      assert(ymwdl1.year() == year{1234});
+      assert(ymwdl1.month() == month{10 - i});
+      assert(ymwdl1.weekday() == Tuesday);
+      assert(ymwdl1.weekday_last() == weekday_last{Tuesday});
+    }
+    // Test the year wraps around.
+    for (unsigned i = 12; i < 15; ++i) {
+      year_month_weekday_last ymwdl1 = ymwdl - months{i};
+      assert(ymwdl1.year() == year{1233});
+      assert(ymwdl1.month() == month{10 - i + 12});
+      assert(ymwdl1.weekday() == Tuesday);
+      assert(ymwdl1.weekday_last() == weekday_last{Tuesday});
     }
   }
 
diff --git a/libcxx/test/std/time/time.cal/time.cal.ymwdlast/time.cal.ymwdlast.nonmembers/plus.pass.cpp b/libcxx/test/std/time/time.cal/time.cal.ymwdlast/time.cal.ymwdlast.nonmembers/plus.pass.cpp
index 7529180d5731b3..560b2fcde05568 100644
--- a/libcxx/test/std/time/time.cal/time.cal.ymwdlast/time.cal.ymwdlast.nonmembers/plus.pass.cpp
+++ b/libcxx/test/std/time/time.cal/time.cal.ymwdlast/time.cal.ymwdlast.nonmembers/plus.pass.cpp
@@ -41,37 +41,50 @@ constexpr bool test() {
   constexpr month January   = std::chrono::January;
 
   { // year_month_weekday_last + months
-    year_month_weekday_last ym{year{1234}, January, weekday_last{Tuesday}};
-    for (int i = 0; i <= 10; ++i) // TODO test wrap-around
-    {
-      year_month_weekday_last ym1 = ym + months{i};
-      year_month_weekday_last ym2 = months{i} + ym;
-      assert(ym1.year() == year(1234));
-      assert(ym2.year() == year(1234));
-      assert(ym1.month() == month(1 + i));
-      assert(ym2.month() == month(1 + i));
-      assert(ym1.weekday() == Tuesday);
-      assert(ym2.weekday() == Tuesday);
-      assert(ym1.weekday_last() == weekday_last{Tuesday});
-      assert(ym2.weekday_last() == weekday_last{Tuesday});
-      assert(ym1 == ym2);
+    year_month_weekday_last ymwdl{year{1234}, January, weekday_last{Tuesday}};
+    for (int i = 0; i <= 10; ++i) {
+      year_month_weekday_last ymwdl1 = ymwdl + months{i};
+      year_month_weekday_last ymwdl2 = months{i} + ymwdl;
+      assert(ymwdl1.year() == year{1234});
+      assert(ymwdl2.year() == year{1234});
+      assert(ymwdl1.month() == month(1 + i));
+      assert(ymwdl2.month() == month(1 + i));
+      assert(ymwdl1.weekday() == Tuesday);
+      assert(ymwdl2.weekday() == Tuesday);
+      assert(ymwdl1.weekday_last() == weekday_last{Tuesday});
+      assert(ymwdl2.weekday_last() == weekday_last{Tuesday});
+      assert(ymwdl1 == ymwdl2);
+    }
+    // Test the year wraps around.
+    for (int i = 12; i <= 15; ++i) {
+      year_month_weekday_last ymwdl1 = ymwdl + months{i};
+      year_month_weekday_last ymwdl2 = months{i} + ymwdl;
+      assert(ymwdl1.year() == year{1235});
+      assert(ymwdl2.year() == year{1235});
+      assert(ymwdl1.month() == month(1 + i - 12));
+      assert(ymwdl2.month() == month(1 + i - 12));
+      assert(ymwdl1.weekday() == Tuesday);
+      assert(ymwdl2.weekday() == Tuesday);
+      assert(ymwdl1.weekday_last() == weekday_last{Tuesday});
+      assert(ymwdl2.weekday_last() == weekday_last{Tuesday});
+      assert(ymwdl1 == ymwdl2);
     }
   }
 
   { // year_month_weekday_last + years
-    year_month_weekday_last ym{year{1234}, std::chrono::January, weekday_last{Tuesday}};
+    year_month_weekday_last ymwdl{year{1234}, std::chrono::January, weekday_last{Tuesday}};
     for (int i = 0; i <= 10; ++i) {
-      year_month_weekday_last ym1 = ym + years{i};
-      year_month_weekday_last ym2 = years{i} + ym;
-      assert(ym1.year() == year(1234 + i));
-      assert(ym2.year() == year(1234 + i));
-      assert(ym1.month() == January);
-      assert(ym2.month() == January);
-      assert(ym1.weekday() == Tuesday);
-      assert(ym2.weekday() == Tuesday);
-      assert(ym1.weekday_last() == weekday_last{Tuesday});
-      assert(ym2.weekday_last() == weekday_last{Tuesday});
-      assert(ym1 == ym2);
+      year_month_weekday_last ymwdl1 = ymwdl + years{i};
+      year_month_weekday_last ymwdl2 = years{i} + ymwdl;
+      assert(ymwdl1.year() == year(1234 + i));
+      assert(ymwdl2.year() == year(1234 + i));
+      assert(ymwdl1.month() == January);
+      assert(ymwdl2.month() == January);
+      assert(ymwdl1.weekday() == Tuesday);
+      assert(ymwdl2.weekday() == Tuesday);
+      assert(ymwdl1.weekday_last() == weekday_last{Tuesday});
+      assert(ymwdl2.weekday_last() == weekday_last{Tuesday});
+      assert(ymwdl1 == ymwdl2);
     }
   }
 



More information about the libcxx-commits mailing list