diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index 4bc01b8f4ddb0..50233a9c7cd78 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -26,9 +26,11 @@ ) from pandas._libs.tslibs import ( Resolution, + periods_per_day, timezones, to_offset, ) +from pandas._libs.tslibs.dtypes import NpyDatetimeUnit from pandas._libs.tslibs.offsets import prefix_mapping from pandas._typing import ( Dtype, @@ -448,7 +450,7 @@ def _maybe_utc_convert(self, other: Index) -> tuple[DatetimeIndex, Index]: # -------------------------------------------------------------------- - def _get_time_micros(self) -> np.ndarray: + def _get_time_micros(self) -> npt.NDArray[np.int64]: """ Return the number of microseconds since midnight. @@ -458,8 +460,20 @@ def _get_time_micros(self) -> np.ndarray: """ values = self._data._local_timestamps() - nanos = values % (24 * 3600 * 1_000_000_000) - micros = nanos // 1000 + reso = self._data._reso + ppd = periods_per_day(reso) + + frac = values % ppd + if reso == NpyDatetimeUnit.NPY_FR_ns.value: + micros = frac // 1000 + elif reso == NpyDatetimeUnit.NPY_FR_us.value: + micros = frac + elif reso == NpyDatetimeUnit.NPY_FR_ms.value: + micros = frac * 1000 + elif reso == NpyDatetimeUnit.NPY_FR_s.value: + micros = frac * 1_000_000 + else: # pragma: no cover + raise NotImplementedError(reso) micros[self._isnan] = -1 return micros diff --git a/pandas/tests/indexes/datetimes/test_indexing.py b/pandas/tests/indexes/datetimes/test_indexing.py index b8f72a8c1f988..a203fee5b3a61 100644 --- a/pandas/tests/indexes/datetimes/test_indexing.py +++ b/pandas/tests/indexes/datetimes/test_indexing.py @@ -777,3 +777,32 @@ def test_indexer_between_time(self): msg = r"Cannot convert arg \[datetime\.datetime\(2010, 1, 2, 1, 0\)\] to a time" with pytest.raises(ValueError, match=msg): rng.indexer_between_time(datetime(2010, 1, 2, 1), datetime(2010, 1, 2, 5)) + + @pytest.mark.parametrize("unit", ["us", "ms", "s"]) + def test_indexer_between_time_non_nano(self, unit): + # For simple cases like this, the non-nano indexer_between_time + # should match the nano result + + rng = date_range("1/1/2000", "1/5/2000", freq="5min") + arr_nano = rng._data._ndarray + + arr = arr_nano.astype(f"M8[{unit}]") + + dta = type(rng._data)._simple_new(arr, dtype=arr.dtype) + dti = DatetimeIndex(dta) + assert dti.dtype == arr.dtype + + tic = time(1, 25) + toc = time(2, 29) + + result = dti.indexer_between_time(tic, toc) + expected = rng.indexer_between_time(tic, toc) + tm.assert_numpy_array_equal(result, expected) + + # case with non-zero micros in arguments + tic = time(1, 25, 0, 45678) + toc = time(2, 29, 0, 1234) + + result = dti.indexer_between_time(tic, toc) + expected = rng.indexer_between_time(tic, toc) + tm.assert_numpy_array_equal(result, expected)