Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
2 changes: 1 addition & 1 deletion doc/source/whatsnew/v3.1.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ Timedelta
^^^^^^^^^
- Bug in :class:`DateOffset` where ``DateOffset(1)`` and ``DateOffset(days=1)`` returned different results near daylight saving time transitions (:issue:`61862`)
- Bug in :func:`to_timedelta` where passing ``np.str_`` objects would fail in Cython string parsing (:issue:`48974`)
-
- Fixed regression in :meth:`Timedelta.round`, :meth:`Timedelta.floor`, and :meth:`Timedelta.ceil` raising ``ZeroDivisionError`` for sub-second ``freq`` (:issue:`64828`)

Timezones
^^^^^^^^^
Expand Down
22 changes: 16 additions & 6 deletions pandas/_libs/tslibs/timedeltas.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ import collections
import re
import warnings

from pandas._libs.tslibs.offsets import Day
from pandas._libs.tslibs.offsets import (
Day,
to_offset,
)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think to_offset needs to be a cimport or else you get a circular import problem

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jbrockmendel I applied it. Thank you!

from pandas.util._decorators import set_module
from pandas.util._exceptions import find_stack_level

Expand Down Expand Up @@ -2355,17 +2358,24 @@ class Timedelta(_Timedelta):
@cython.cdivision(True)
def _round(self, freq, mode):
cdef:
int64_t result, unit
int64_t result, nanos
ndarray[int64_t] arr

unit = get_unit_for_round(freq, self._creso)
freq_arg = freq
freq = to_offset(freq, is_period=False)
nanos = get_unit_for_round(freq, self._creso)
if nanos == 0:
if freq.nanos == 0:
raise ValueError("Division by zero in rounding")

# e.g. self.unit == "s" and sub-second freq
return self

arr = np.array([self._value], dtype="i8")
try:
result = round_nsint64(arr, mode, unit)[0]
result = round_nsint64(arr, mode, nanos)[0]
except OverflowError as err:
raise OutOfBoundsTimedelta(
f"Cannot round {self} to freq={freq} without overflow"
f"Cannot round {self} to freq={freq_arg} without overflow"
) from err
return Timedelta._from_value_and_reso(result, self._creso)

Expand Down
8 changes: 8 additions & 0 deletions pandas/tests/scalar/timedelta/methods/test_round.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,3 +185,11 @@ def test_round_non_nano(self, unit):
res = td.ceil("min")
assert res == Timedelta("1 days 02:35:00")
assert res._creso == td._creso

def test_round_freq_finer_than_resolution(self):
# GH#64828
td = Timedelta(1.0, unit="days").as_unit("s")
assert td.unit == "s"
assert td.round("100ms") == Timedelta("1 days 00:00:00")
assert td.floor("100ms") == Timedelta("1 days 00:00:00")
assert td.ceil("100ms") == Timedelta("1 days 00:00:00")
Loading