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
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 24 additions & 4 deletions libcxx/include/__chrono/year_month.h
Original file line number Diff line number Diff line change
Expand Up @@ -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(); }
};

Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,19 @@

#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
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) {
Expand All @@ -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) {
Expand All @@ -89,7 +94,6 @@ constexpr bool test_ym_plus_m() {
assert(ym2.month() == month(1 + i % 12));
assert(ym1 == ym2);
}

return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
Loading