Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
1 change: 1 addition & 0 deletions doc/source/whatsnew/v3.0.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1131,6 +1131,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 :meth:`DataFrame.cumsum` which was raising ``IndexError`` if dtype is ``timedelta64[ns]`` (:issue:`57956`)
- Bug in adding or subtracting a :class:`Timedelta` object with non-nanosecond unit to a python ``datetime.datetime`` object giving incorrect results; this now works correctly for Timedeltas inside the ``datetime.timedelta`` implementation bounds (:issue:`53643`)
- Bug in multiplication operations with ``timedelta64`` dtype failing to raise ``TypeError`` when multiplying by ``bool`` objects or dtypes (:issue:`58054`)
- Bug in multiplication operations with ``timedelta64`` dtype incorrectly raising when multiplying by numpy-nullable dtypes or pyarrow integer dtypes (:issue:`58054`)

Expand Down
18 changes: 16 additions & 2 deletions pandas/_libs/tslibs/timedeltas.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -1022,9 +1022,23 @@ cdef _timedelta_from_value_and_reso(cls, int64_t value, NPY_DATETIMEUNIT reso):
elif reso == NPY_DATETIMEUNIT.NPY_FR_us:
td_base = _Timedelta.__new__(cls, microseconds=int(value))
elif reso == NPY_DATETIMEUNIT.NPY_FR_ms:
td_base = _Timedelta.__new__(cls, milliseconds=0)
if -86_399_999_913_600_000 <= value <= 86_400_000_000_000_000:
# i.e. we are in range for pytimedelta. By passing the
# 'correct' value here we can
# make pydatetime + Timedelta operations work correctly,
# xref GH#53643
td_base = _Timedelta.__new__(cls, milliseconds=value)
else:
td_base = _Timedelta.__new__(cls, milliseconds=0)
elif reso == NPY_DATETIMEUNIT.NPY_FR_s:
td_base = _Timedelta.__new__(cls, seconds=0)
if -86_399_999_913_600 <= value <= 86_400_000_000_000:
# i.e. we are in range for pytimedelta. By passing the
# 'correct' value here we can
# make pydatetime + Timedelta operations work correctly,
# xref GH#53643
td_base = _Timedelta.__new__(cls, seconds=value)
else:
td_base = _Timedelta.__new__(cls, seconds=0)
# Other resolutions are disabled but could potentially be implemented here:
# elif reso == NPY_DATETIMEUNIT.NPY_FR_m:
# td_base = _Timedelta.__new__(Timedelta, minutes=int(value))
Expand Down
15 changes: 15 additions & 0 deletions pandas/tests/scalar/timedelta/test_arithmetic.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,21 @@ class TestTimedeltaAdditionSubtraction:
__sub__, __rsub__
"""

def test_td_add_sub_pydatetime(self, unit):
# GH#53643
td = Timedelta(hours=23).as_unit(unit)
dt = datetime(2016, 1, 1)

expected = datetime(2016, 1, 1, 23)
result = dt + td
assert result == expected
result = td + dt
assert result == expected

expected = datetime(2015, 12, 31, 1)
result = dt - td
assert result == expected

@pytest.mark.parametrize(
"ten_seconds",
[
Expand Down
Loading