Skip to content
Open
Show file tree
Hide file tree
Changes from all 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 value > -86_399_999_913_600_000 and 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 value > -86_399_999_913_600 and 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