Skip to content

Commit c0a1e2a

Browse files
authored
BUG: Week.__add__ with non-nano (pandas-dev#55583)
* BUG: Week.__add__ with non-nano * GH ref * typo fixup
1 parent c31c6ba commit c0a1e2a

File tree

4 files changed

+22
-12
lines changed

4 files changed

+22
-12
lines changed

doc/source/whatsnew/v2.2.0.rst

+2-1
Original file line numberDiff line numberDiff line change
@@ -301,8 +301,9 @@ Datetimelike
301301
^^^^^^^^^^^^
302302
- Bug in :meth:`DatetimeIndex.union` returning object dtype for tz-aware indexes with the same timezone but different units (:issue:`55238`)
303303
- Bug in :meth:`Tick.delta` with very large ticks raising ``OverflowError`` instead of ``OutOfBoundsTimedelta`` (:issue:`55503`)
304+
- 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`)
304305
- 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`)
305-
306+
-
306307

307308
Timedelta
308309
^^^^^^^^^

pandas/_libs/tslibs/offsets.pyi

+4-1
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,10 @@ def roll_qtrday(
277277
INVALID_FREQ_ERR_MSG: Literal["Invalid frequency: {0}"]
278278

279279
def shift_months(
280-
dtindex: npt.NDArray[np.int64], months: int, day_opt: str | None = ...
280+
dtindex: npt.NDArray[np.int64],
281+
months: int,
282+
day_opt: str | None = ...,
283+
reso: int = ...,
281284
) -> npt.NDArray[np.int64]: ...
282285

283286
_offset_map: dict[str, BaseOffset]

pandas/_libs/tslibs/offsets.pyx

+2-1
Original file line numberDiff line numberDiff line change
@@ -3272,7 +3272,8 @@ cdef class Week(SingleConstructorOffset):
32723272
def _apply_array(self, dtarr):
32733273
if self.weekday is None:
32743274
td = timedelta(days=7 * self.n)
3275-
td64 = np.timedelta64(td, "ns")
3275+
unit = np.datetime_data(dtarr.dtype)[0]
3276+
td64 = np.timedelta64(td, unit)
32763277
return dtarr + td64
32773278
else:
32783279
reso = get_unit_from_dtype(dtarr.dtype)

pandas/tests/arithmetic/test_datetime64.py

+14-9
Original file line numberDiff line numberDiff line change
@@ -1415,8 +1415,9 @@ def test_dt64arr_add_sub_relativedelta_offsets(self, box_with_array):
14151415
)
14161416
@pytest.mark.parametrize("normalize", [True, False])
14171417
@pytest.mark.parametrize("n", [0, 5])
1418+
@pytest.mark.parametrize("unit", ["s", "ms", "us", "ns"])
14181419
def test_dt64arr_add_sub_DateOffsets(
1419-
self, box_with_array, n, normalize, cls_and_kwargs
1420+
self, box_with_array, n, normalize, cls_and_kwargs, unit
14201421
):
14211422
# GH#10699
14221423
# assert vectorized operation matches pointwise operations
@@ -1449,7 +1450,7 @@ def test_dt64arr_add_sub_DateOffsets(
14491450
Timestamp("2000-05-15"),
14501451
Timestamp("2001-06-15"),
14511452
]
1452-
)
1453+
).as_unit(unit)
14531454
vec = tm.box_expected(vec, box_with_array)
14541455
vec_items = vec.iloc[0] if box_with_array is pd.DataFrame else vec
14551456

@@ -1461,15 +1462,16 @@ def test_dt64arr_add_sub_DateOffsets(
14611462

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

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

1468-
expected = DatetimeIndex([x - offset for x in vec_items])
1470+
expected = DatetimeIndex([x - offset for x in vec_items]).as_unit(unit)
14691471
expected = tm.box_expected(expected, box_with_array)
14701472
tm.assert_equal(expected, vec - offset)
14711473

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

23932395
@pytest.mark.parametrize("years", [-1, 0, 1])
23942396
@pytest.mark.parametrize("months", [-2, 0, 2])
2395-
def test_shift_months(years, months):
2397+
@pytest.mark.parametrize("unit", ["s", "ms", "us", "ns"])
2398+
def test_shift_months(years, months, unit):
23962399
dti = DatetimeIndex(
23972400
[
23982401
Timestamp("2000-01-05 00:15:00"),
@@ -2401,11 +2404,13 @@ def test_shift_months(years, months):
24012404
Timestamp("2000-02-29"),
24022405
Timestamp("2000-12-31"),
24032406
]
2404-
)
2405-
actual = DatetimeIndex(shift_months(dti.asi8, years * 12 + months))
2407+
).as_unit(unit)
2408+
shifted = shift_months(dti.asi8, years * 12 + months, reso=dti._data._creso)
2409+
shifted_dt64 = shifted.view(f"M8[{dti.unit}]")
2410+
actual = DatetimeIndex(shifted_dt64)
24062411

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

24112416

0 commit comments

Comments
 (0)