diff --git a/src/pendulum/duration.py b/src/pendulum/duration.py index d6cc0657d..262667815 100644 --- a/src/pendulum/duration.py +++ b/src/pendulum/duration.py @@ -15,6 +15,8 @@ if TYPE_CHECKING: + from typing import SupportsIndex + from typing_extensions import Self @@ -470,6 +472,9 @@ def __deepcopy__(self, _: dict[int, Self]) -> Self: weeks=self.weeks, ) + def __reduce_ex__(self, protocol: SupportsIndex) -> tuple[object, ...]: + return (*super().__reduce_ex__(protocol), self.__dict__) + Duration.min = Duration(days=-999999999) Duration.max = Duration( diff --git a/tests/duration/test_behavior.py b/tests/duration/test_behavior.py index cf60282fa..55bef5d07 100644 --- a/tests/duration/test_behavior.py +++ b/tests/duration/test_behavior.py @@ -2,6 +2,7 @@ import pickle +from copy import copy from copy import deepcopy from datetime import timedelta @@ -20,6 +21,37 @@ def test_pickle() -> None: assert it == it2 +@pytest.mark.parametrize("protocol", range(pickle.HIGHEST_PROTOCOL + 1)) +@pytest.mark.parametrize("years, months", [(2, 3), (-2, -3), (1, -2)]) +def test_pickle_preserves_calendar_components( + protocol: int, years: int, months: int +) -> None: + original = pendulum.duration( + years=years, months=months, weeks=1, days=2, seconds=37, microseconds=123 + ) + restored = pickle.loads(pickle.dumps(original, protocol=protocol)) + + assert restored == original + assert restored.years == original.years + assert restored.months == original.months + assert restored.weeks == original.weeks + assert restored.remaining_days == original.remaining_days + assert restored.seconds == original.seconds + assert restored.microseconds == original.microseconds + assert str(restored) == str(original) + + +def test_shallow_copy_preserves_calendar_components() -> None: + original = pendulum.duration(years=1, months=2, days=9) + copied = copy(original) + + assert copied is not original + assert copied == original + assert copied.years == original.years + assert copied.months == original.months + assert str(copied) == str(original) + + def test_comparison_to_timedelta() -> None: duration = pendulum.duration(days=3)