Skip to content

Commit

Permalink
BUG: Week.__add__ with non-nano (#55583)
Browse files Browse the repository at this point in the history
* BUG: Week.__add__ with non-nano

* GH ref

* typo fixup
  • Loading branch information
jbrockmendel authored Oct 19, 2023
1 parent c31c6ba commit c0a1e2a
Show file tree
Hide file tree
Showing 4 changed files with 22 additions and 12 deletions.
3 changes: 2 additions & 1 deletion doc/source/whatsnew/v2.2.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -301,8 +301,9 @@ Datetimelike
^^^^^^^^^^^^
- Bug in :meth:`DatetimeIndex.union` returning object dtype for tz-aware indexes with the same timezone but different units (:issue:`55238`)
- Bug in :meth:`Tick.delta` with very large ticks raising ``OverflowError`` instead of ``OutOfBoundsTimedelta`` (:issue:`55503`)
- Bug in adding or subtracting a :class:`Week` offset to a ``datetime64`` :class:`Series`, :class:`Index`, or :class:`DataFrame` column with non-nanosecond resolution returning incorrect results (:issue:`55583`)
- Bug in addition or subtraction of very large :class:`Tick` objects with :class:`Timestamp` or :class:`Timedelta` objects raising ``OverflowError`` instead of ``OutOfBoundsTimedelta`` (:issue:`55503`)

-

Timedelta
^^^^^^^^^
Expand Down
5 changes: 4 additions & 1 deletion pandas/_libs/tslibs/offsets.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,10 @@ def roll_qtrday(
INVALID_FREQ_ERR_MSG: Literal["Invalid frequency: {0}"]

def shift_months(
dtindex: npt.NDArray[np.int64], months: int, day_opt: str | None = ...
dtindex: npt.NDArray[np.int64],
months: int,
day_opt: str | None = ...,
reso: int = ...,
) -> npt.NDArray[np.int64]: ...

_offset_map: dict[str, BaseOffset]
3 changes: 2 additions & 1 deletion pandas/_libs/tslibs/offsets.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -3272,7 +3272,8 @@ cdef class Week(SingleConstructorOffset):
def _apply_array(self, dtarr):
if self.weekday is None:
td = timedelta(days=7 * self.n)
td64 = np.timedelta64(td, "ns")
unit = np.datetime_data(dtarr.dtype)[0]
td64 = np.timedelta64(td, unit)
return dtarr + td64
else:
reso = get_unit_from_dtype(dtarr.dtype)
Expand Down
23 changes: 14 additions & 9 deletions pandas/tests/arithmetic/test_datetime64.py
Original file line number Diff line number Diff line change
Expand Up @@ -1415,8 +1415,9 @@ def test_dt64arr_add_sub_relativedelta_offsets(self, box_with_array):
)
@pytest.mark.parametrize("normalize", [True, False])
@pytest.mark.parametrize("n", [0, 5])
@pytest.mark.parametrize("unit", ["s", "ms", "us", "ns"])
def test_dt64arr_add_sub_DateOffsets(
self, box_with_array, n, normalize, cls_and_kwargs
self, box_with_array, n, normalize, cls_and_kwargs, unit
):
# GH#10699
# assert vectorized operation matches pointwise operations
Expand Down Expand Up @@ -1449,7 +1450,7 @@ def test_dt64arr_add_sub_DateOffsets(
Timestamp("2000-05-15"),
Timestamp("2001-06-15"),
]
)
).as_unit(unit)
vec = tm.box_expected(vec, box_with_array)
vec_items = vec.iloc[0] if box_with_array is pd.DataFrame else vec

Expand All @@ -1461,15 +1462,16 @@ def test_dt64arr_add_sub_DateOffsets(

offset = offset_cls(n, normalize=normalize, **kwargs)

expected = DatetimeIndex([x + offset for x in vec_items])
# TODO(GH#55564): as_unit will be unnecessary
expected = DatetimeIndex([x + offset for x in vec_items]).as_unit(unit)
expected = tm.box_expected(expected, box_with_array)
tm.assert_equal(expected, vec + offset)

expected = DatetimeIndex([x - offset for x in vec_items])
expected = DatetimeIndex([x - offset for x in vec_items]).as_unit(unit)
expected = tm.box_expected(expected, box_with_array)
tm.assert_equal(expected, vec - offset)

expected = DatetimeIndex([offset + x for x in vec_items])
expected = DatetimeIndex([offset + x for x in vec_items]).as_unit(unit)
expected = tm.box_expected(expected, box_with_array)
tm.assert_equal(expected, offset + vec)
msg = "(bad|unsupported) operand type for unary"
Expand Down Expand Up @@ -2392,7 +2394,8 @@ def test_dti_addsub_object_arraylike(

@pytest.mark.parametrize("years", [-1, 0, 1])
@pytest.mark.parametrize("months", [-2, 0, 2])
def test_shift_months(years, months):
@pytest.mark.parametrize("unit", ["s", "ms", "us", "ns"])
def test_shift_months(years, months, unit):
dti = DatetimeIndex(
[
Timestamp("2000-01-05 00:15:00"),
Expand All @@ -2401,11 +2404,13 @@ def test_shift_months(years, months):
Timestamp("2000-02-29"),
Timestamp("2000-12-31"),
]
)
actual = DatetimeIndex(shift_months(dti.asi8, years * 12 + months))
).as_unit(unit)
shifted = shift_months(dti.asi8, years * 12 + months, reso=dti._data._creso)
shifted_dt64 = shifted.view(f"M8[{dti.unit}]")
actual = DatetimeIndex(shifted_dt64)

raw = [x + pd.offsets.DateOffset(years=years, months=months) for x in dti]
expected = DatetimeIndex(raw)
expected = DatetimeIndex(raw).as_unit(dti.unit)
tm.assert_index_equal(actual, expected)


Expand Down

0 comments on commit c0a1e2a

Please sign in to comment.