From 12d5f0969365218fcfe8c3c4b75c9befeedfdb7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Thu, 14 Dec 2023 01:03:11 +0100 Subject: [PATCH] Fix errors where hours and days were not handled correctly when adding durations Fixes #768 --- src/pendulum/datetime.py | 4 +--- src/pendulum/duration.py | 16 +++++++++++++++- tests/datetime/test_add.py | 14 +++++++++++++- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/pendulum/datetime.py b/src/pendulum/datetime.py index 6452f3b8..9fb438c9 100644 --- a/src/pendulum/datetime.py +++ b/src/pendulum/datetime.py @@ -685,9 +685,7 @@ def _add_timedelta_(self, delta: datetime.timedelta) -> Self: microseconds=delta.microseconds, ) elif isinstance(delta, pendulum.Duration): - return self.add( - years=delta.years, months=delta.months, seconds=delta._total - ) + return self.add(**delta._signature) # type: ignore[attr-defined] return self.add(seconds=delta.total_seconds()) diff --git a/src/pendulum/duration.py b/src/pendulum/duration.py index 962f923c..a4875fca 100644 --- a/src/pendulum/duration.py +++ b/src/pendulum/duration.py @@ -112,6 +112,17 @@ def __new__( self._months = months self._years = years + self._signature = { # type: ignore[attr-defined] + "years": years, + "months": months, + "weeks": weeks, + "days": days, + "hours": hours, + "minutes": minutes, + "seconds": seconds, + "microseconds": microseconds + milliseconds * 1000, + } + return self def total_minutes(self) -> float: @@ -440,7 +451,10 @@ def __mod__(self, other: timedelta) -> Self: def __divmod__(self, other: timedelta) -> tuple[int, Duration]: if isinstance(other, timedelta): - q, r = divmod(self._to_microseconds(), other._to_microseconds()) # type: ignore[attr-defined] # noqa: E501 + q, r = divmod( + self._to_microseconds(), + other._to_microseconds(), # type: ignore[attr-defined] + ) return q, self.__class__(0, 0, r) diff --git a/tests/datetime/test_add.py b/tests/datetime/test_add.py index 6d436569..409f5bd4 100644 --- a/tests/datetime/test_add.py +++ b/tests/datetime/test_add.py @@ -252,13 +252,25 @@ def test_add_time_to_new_transition_repeated_big(): assert not dt.is_dst() -def test_add_interval(): +def test_add_duration_across_transition(): dt = pendulum.datetime(2017, 3, 11, 10, 45, tz="America/Los_Angeles") new = dt + pendulum.duration(hours=24) assert_datetime(new, 2017, 3, 12, 11, 45) +def test_add_duration_across_transition_days(): + dt = pendulum.datetime(2017, 3, 11, 10, 45, tz="America/Los_Angeles") + new = dt + pendulum.duration(days=1) + + assert_datetime(new, 2017, 3, 12, 10, 45) + + dt = pendulum.datetime(2023, 11, 5, 0, 0, tz="America/Chicago") + new = dt + pendulum.duration(days=1) + + assert_datetime(new, 2023, 11, 6, 0, 0) + + def test_interval_over_midnight_tz(): start = pendulum.datetime(2018, 2, 25, tz="Europe/Paris") end = start.add(hours=1)