Skip to content

Commit 80029d5

Browse files
committed
Preserve calendar units in reflected Date addition
1 parent e06dace commit 80029d5

2 files changed

Lines changed: 50 additions & 0 deletions

File tree

‎src/pendulum/date.py‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,9 @@ def __add__(self, other: timedelta) -> Self:
260260

261261
return self._add_timedelta(other)
262262

263+
def __radd__(self, other: timedelta) -> Self:
264+
return self.__add__(other)
265+
263266
@overload # type: ignore[override] # this is only needed because of Python 3.7
264267
def __sub__(self, __delta: timedelta) -> Self: ...
265268

‎tests/date/test_add.py‎

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,53 @@ def test_add_duration():
7878
assert_date(new, 2017, 7, 2)
7979

8080

81+
@pytest.mark.parametrize(
82+
"date, duration, expected",
83+
[
84+
((2024, 1, 31), {"months": 1}, (2024, 2, 29)),
85+
((2023, 1, 31), {"months": 1}, (2023, 2, 28)),
86+
((2024, 3, 31), {"months": -1}, (2024, 2, 29)),
87+
((2023, 3, 31), {"months": -1}, (2023, 2, 28)),
88+
((2023, 3, 1), {"years": 1}, (2024, 3, 1)),
89+
((2024, 2, 29), {"years": -1}, (2023, 2, 28)),
90+
((2023, 1, 31), {"years": 2, "months": 3, "days": 18}, (2025, 5, 18)),
91+
],
92+
)
93+
def test_add_duration_reflected(date, duration, expected):
94+
d = pendulum.Date(*date)
95+
delta = pendulum.duration(**duration)
96+
97+
result = delta + d
98+
99+
assert isinstance(result, pendulum.Date)
100+
assert_date(result, *expected)
101+
assert result == d + delta
102+
103+
104+
@pytest.mark.parametrize(
105+
"delta",
106+
[timedelta(days=18), timedelta(seconds=-1), timedelta(), timedelta(seconds=1)],
107+
)
108+
def test_add_timedelta_reflected(delta):
109+
d = pendulum.Date(2024, 1, 31)
110+
111+
result = delta + d
112+
113+
assert isinstance(result, pendulum.Date)
114+
assert result == d + delta
115+
116+
117+
def test_add_duration_reflected_preserves_date_subclass():
118+
class CustomDate(pendulum.Date):
119+
pass
120+
121+
d = CustomDate(2024, 1, 31)
122+
result = pendulum.duration(months=1) + d
123+
124+
assert isinstance(result, CustomDate)
125+
assert_date(result, 2024, 2, 29)
126+
127+
81128
def test_addition_invalid_type():
82129
d = pendulum.date(2015, 3, 14)
83130

0 commit comments

Comments
 (0)