diff --git a/doc/source/whatsnew/v3.0.0.rst b/doc/source/whatsnew/v3.0.0.rst index 1383202154f04..7b2ecffa95ba1 100644 --- a/doc/source/whatsnew/v3.0.0.rst +++ b/doc/source/whatsnew/v3.0.0.rst @@ -721,6 +721,7 @@ Timedelta ^^^^^^^^^ - Accuracy improvement in :meth:`Timedelta.to_pytimedelta` to round microseconds consistently for large nanosecond based Timedelta (:issue:`57841`) - Bug in :class:`Timedelta` constructor failing to raise when passed an invalid keyword (:issue:`53801`) +- Bug in :class:`pandas.tseries.offsets.DateOffset` where ``DateOffset(1)`` and ``DateOffset(days=1)`` returned different results near daylight saving time transitions. (:issue:`61862`) - Bug in :meth:`DataFrame.cumsum` which was raising ``IndexError`` if dtype is ``timedelta64[ns]`` (:issue:`57956`) Timezones diff --git a/pandas/_libs/tslibs/offsets.pyx b/pandas/_libs/tslibs/offsets.pyx index 87214c3758d5c..d465defd0e971 100644 --- a/pandas/_libs/tslibs/offsets.pyx +++ b/pandas/_libs/tslibs/offsets.pyx @@ -18,6 +18,8 @@ from cpython.datetime cimport ( import warnings +from dateutil.relativedelta import relativedelta + import_datetime() import numpy as np @@ -283,8 +285,9 @@ _relativedelta_kwds = {"years", "months", "weeks", "days", "year", "month", cdef _determine_offset(kwds): if not kwds: - # GH 45643/45890: (historically) defaults to 1 day - return timedelta(days=1), False + # GH 45643, 45890: (historically) defaults to 1 day + # GH 61870: changed from timedelta to relativedelta + return relativedelta(days=1), True if "millisecond" in kwds: raise NotImplementedError( @@ -325,8 +328,6 @@ cdef _determine_offset(kwds): kwds_no_nanos["microseconds"] = kwds_no_nanos.get("microseconds", 0) + micro if all(k in kwds_use_relativedelta for k in kwds_no_nanos): - from dateutil.relativedelta import relativedelta - return relativedelta(**kwds_no_nanos), True raise ValueError( diff --git a/pandas/tests/tseries/offsets/test_offsets.py b/pandas/tests/tseries/offsets/test_offsets.py index 0b2e66a2b3a0d..70bd4de55a38b 100644 --- a/pandas/tests/tseries/offsets/test_offsets.py +++ b/pandas/tests/tseries/offsets/test_offsets.py @@ -9,6 +9,7 @@ timedelta, ) +from dateutil.relativedelta import relativedelta import numpy as np import pytest @@ -1095,9 +1096,9 @@ def test_dateoffset_misc(): @pytest.mark.parametrize("n", [-1, 1, 3]) def test_construct_int_arg_no_kwargs_assumed_days(n): - # GH 45890, 45643 + # GH 45643, 45890, 61870 offset = DateOffset(n) - assert offset._offset == timedelta(1) + assert offset._offset == relativedelta(days=1) result = Timestamp(2022, 1, 2) + offset expected = Timestamp(2022, 1, 2 + n) assert result == expected @@ -1227,3 +1228,12 @@ def test_is_yqm_start_end(): def test_multiply_dateoffset_typeerror(left, right): with pytest.raises(TypeError, match="Cannot multiply"): left * right + + +def test_dateoffset_days_vs_n_near_dst_transition(): + # GH 61870 + ts = Timestamp("2022-10-30", tz="Europe/Brussels") + + offset_days = ts + offsets.DateOffset(days=1) + offset_n = ts + offsets.DateOffset(1) + assert offset_days == offset_n