Skip to content

Commit b1e77f9

Browse files
jbrockmendeljreback
authored andcommitted
BUG: DTI/TDI .insert accepting incorrectly-dtyped NaT (#30754)
1 parent 6b3df29 commit b1e77f9

File tree

4 files changed

+46
-14
lines changed

4 files changed

+46
-14
lines changed

pandas/core/indexes/datetimes.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
from pandas.core.dtypes.common import _NS_DTYPE, is_float, is_integer, is_scalar
1313
from pandas.core.dtypes.dtypes import DatetimeTZDtype
14-
from pandas.core.dtypes.missing import isna
14+
from pandas.core.dtypes.missing import is_valid_nat_for_dtype, isna
1515

1616
from pandas.core.accessor import delegate_names
1717
from pandas.core.arrays.datetimes import (
@@ -922,9 +922,14 @@ def insert(self, loc, item):
922922
-------
923923
new_index : Index
924924
"""
925-
if is_scalar(item) and isna(item):
925+
if is_valid_nat_for_dtype(item, self.dtype):
926926
# GH 18295
927927
item = self._na_value
928+
elif is_scalar(item) and isna(item):
929+
# i.e. timedeltat64("NaT")
930+
raise TypeError(
931+
f"cannot insert {type(self).__name__} with incompatible label"
932+
)
928933

929934
freq = None
930935

pandas/core/indexes/timedeltas.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
is_timedelta64_ns_dtype,
1717
pandas_dtype,
1818
)
19-
from pandas.core.dtypes.missing import isna
19+
from pandas.core.dtypes.missing import is_valid_nat_for_dtype, isna
2020

2121
from pandas.core.accessor import delegate_names
2222
from pandas.core.arrays import datetimelike as dtl
@@ -397,15 +397,16 @@ def insert(self, loc, item):
397397
new_index : Index
398398
"""
399399
# try to convert if possible
400-
if _is_convertible_to_td(item):
401-
try:
402-
item = Timedelta(item)
403-
except ValueError:
404-
# e.g. str that can't be parsed to timedelta
405-
pass
406-
elif is_scalar(item) and isna(item):
400+
if isinstance(item, self._data._recognized_scalars):
401+
item = Timedelta(item)
402+
elif is_valid_nat_for_dtype(item, self.dtype):
407403
# GH 18295
408404
item = self._na_value
405+
elif is_scalar(item) and isna(item):
406+
# i.e. datetime64("NaT")
407+
raise TypeError(
408+
f"cannot insert {type(self).__name__} with incompatible label"
409+
)
409410

410411
freq = None
411412
if isinstance(item, Timedelta) or (is_scalar(item) and isna(item)):

pandas/tests/indexes/datetimes/test_indexing.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,9 @@ def test_take_fill_value_with_timezone(self):
317317

318318

319319
class TestDatetimeIndex:
320-
@pytest.mark.parametrize("null", [None, np.nan, pd.NaT])
320+
@pytest.mark.parametrize(
321+
"null", [None, np.nan, np.datetime64("NaT"), pd.NaT, pd.NA]
322+
)
321323
@pytest.mark.parametrize("tz", [None, "UTC", "US/Eastern"])
322324
def test_insert_nat(self, tz, null):
323325
# GH#16537, GH#18295 (test missing)
@@ -326,6 +328,12 @@ def test_insert_nat(self, tz, null):
326328
res = idx.insert(0, null)
327329
tm.assert_index_equal(res, expected)
328330

331+
@pytest.mark.parametrize("tz", [None, "UTC", "US/Eastern"])
332+
def test_insert_invalid_na(self, tz):
333+
idx = pd.DatetimeIndex(["2017-01-01"], tz=tz)
334+
with pytest.raises(TypeError, match="incompatible label"):
335+
idx.insert(0, np.timedelta64("NaT"))
336+
329337
def test_insert(self):
330338
idx = DatetimeIndex(["2000-01-04", "2000-01-01", "2000-01-02"], name="idx")
331339

pandas/tests/indexes/timedeltas/test_indexing.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -219,11 +219,29 @@ def test_insert(self):
219219
assert result.name == expected.name
220220
assert result.freq == expected.freq
221221

222+
@pytest.mark.parametrize(
223+
"null", [None, np.nan, np.timedelta64("NaT"), pd.NaT, pd.NA]
224+
)
225+
def test_insert_nat(self, null):
222226
# GH 18295 (test missing)
227+
idx = timedelta_range("1day", "3day")
228+
result = idx.insert(1, null)
223229
expected = TimedeltaIndex(["1day", pd.NaT, "2day", "3day"])
224-
for na in (np.nan, pd.NaT, None):
225-
result = timedelta_range("1day", "3day").insert(1, na)
226-
tm.assert_index_equal(result, expected)
230+
tm.assert_index_equal(result, expected)
231+
232+
def test_insert_invalid_na(self):
233+
idx = TimedeltaIndex(["4day", "1day", "2day"], name="idx")
234+
with pytest.raises(TypeError, match="incompatible label"):
235+
idx.insert(0, np.datetime64("NaT"))
236+
237+
def test_insert_dont_cast_strings(self):
238+
# To match DatetimeIndex and PeriodIndex behavior, dont try to
239+
# parse strings to Timedelta
240+
idx = timedelta_range("1day", "3day")
241+
242+
result = idx.insert(0, "1 Day")
243+
assert result.dtype == object
244+
assert result[0] == "1 Day"
227245

228246
def test_delete(self):
229247
idx = timedelta_range(start="1 Days", periods=5, freq="D", name="idx")

0 commit comments

Comments
 (0)