Skip to content

Commit a73ce98

Browse files
jbrockmendeljreback
authored andcommitted
REF: use shareable code for DTI/TDI.insert (#30806)
1 parent 23c1425 commit a73ce98

File tree

6 files changed

+38
-27
lines changed

6 files changed

+38
-27
lines changed

doc/source/whatsnew/v1.0.0.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -872,6 +872,7 @@ Datetimelike
872872
- Bug in :func:`pandas.to_datetime` when called with ``Series`` storing ``IntegerArray`` raising ``TypeError`` instead of returning ``Series`` (:issue:`30050`)
873873
- Bug in :func:`date_range` with custom business hours as ``freq`` and given number of ``periods`` (:issue:`30593`)
874874
- Bug in :class:`PeriodIndex` comparisons with incorrectly casting integers to :class:`Period` objects, inconsistent with the :class:`Period` comparison behavior (:issue:`30722`)
875+
- Bug in :meth:`DatetimeIndex.insert` raising a ``ValueError`` instead of a ``TypeError`` when trying to insert a timezone-aware :class:`Timestamp` into a timezone-naive :class:`DatetimeIndex`, or vice-versa (:issue:`30806`)
875876

876877
Timedelta
877878
^^^^^^^^^

pandas/core/indexes/datetimes.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -895,7 +895,9 @@ def insert(self, loc, item):
895895
-------
896896
new_index : Index
897897
"""
898-
if is_valid_nat_for_dtype(item, self.dtype):
898+
if isinstance(item, self._data._recognized_scalars):
899+
item = self._data._scalar_type(item)
900+
elif is_valid_nat_for_dtype(item, self.dtype):
899901
# GH 18295
900902
item = self._na_value
901903
elif is_scalar(item) and isna(item):
@@ -905,11 +907,8 @@ def insert(self, loc, item):
905907
)
906908

907909
freq = None
908-
909-
if isinstance(item, (datetime, np.datetime64)):
910-
self._assert_can_do_op(item)
911-
if not self._has_same_tz(item) and not isna(item):
912-
raise ValueError("Passed item and index have different timezone")
910+
if isinstance(item, self._data._scalar_type) or item is NaT:
911+
self._data._check_compatible_with(item, setitem=True)
913912

914913
# check freq can be preserved on edge cases
915914
if self.size and self.freq is not None:
@@ -919,19 +918,21 @@ def insert(self, loc, item):
919918
freq = self.freq
920919
elif (loc == len(self)) and item - self.freq == self[-1]:
921920
freq = self.freq
922-
item = _to_M8(item, tz=self.tz)
921+
item = item.asm8
923922

924923
try:
925-
new_dates = np.concatenate(
924+
new_i8s = np.concatenate(
926925
(self[:loc].asi8, [item.view(np.int64)], self[loc:].asi8)
927926
)
928-
return self._shallow_copy(new_dates, freq=freq)
927+
return self._shallow_copy(new_i8s, freq=freq)
929928
except (AttributeError, TypeError):
930929

931930
# fall back to object index
932931
if isinstance(item, str):
933932
return self.astype(object).insert(loc, item)
934-
raise TypeError("cannot insert DatetimeIndex with incompatible label")
933+
raise TypeError(
934+
f"cannot insert {type(self).__name__} with incompatible label"
935+
)
935936

936937
def indexer_at_time(self, time, asof=False):
937938
"""

pandas/core/indexes/timedeltas.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,7 @@ def insert(self, loc, item):
401401
"""
402402
# try to convert if possible
403403
if isinstance(item, self._data._recognized_scalars):
404-
item = Timedelta(item)
404+
item = self._data._scalar_type(item)
405405
elif is_valid_nat_for_dtype(item, self.dtype):
406406
# GH 18295
407407
item = self._na_value
@@ -412,28 +412,32 @@ def insert(self, loc, item):
412412
)
413413

414414
freq = None
415-
if isinstance(item, Timedelta) or (is_scalar(item) and isna(item)):
415+
if isinstance(item, self._data._scalar_type) or item is NaT:
416+
self._data._check_compatible_with(item, setitem=True)
416417

417418
# check freq can be preserved on edge cases
418-
if self.freq is not None:
419-
if (loc == 0 or loc == -len(self)) and item + self.freq == self[0]:
419+
if self.size and self.freq is not None:
420+
if item is NaT:
421+
pass
422+
elif (loc == 0 or loc == -len(self)) and item + self.freq == self[0]:
420423
freq = self.freq
421424
elif (loc == len(self)) and item - self.freq == self[-1]:
422425
freq = self.freq
423-
item = Timedelta(item).asm8.view(_TD_DTYPE)
426+
item = item.asm8
424427

425428
try:
426-
new_tds = np.concatenate(
429+
new_i8s = np.concatenate(
427430
(self[:loc].asi8, [item.view(np.int64)], self[loc:].asi8)
428431
)
429-
return self._shallow_copy(new_tds, freq=freq)
430-
432+
return self._shallow_copy(new_i8s, freq=freq)
431433
except (AttributeError, TypeError):
432434

433435
# fall back to object index
434436
if isinstance(item, str):
435437
return self.astype(object).insert(loc, item)
436-
raise TypeError("cannot insert TimedeltaIndex with incompatible label")
438+
raise TypeError(
439+
f"cannot insert {type(self).__name__} with incompatible label"
440+
)
437441

438442

439443
TimedeltaIndex._add_logical_methods_disabled()

pandas/tests/arithmetic/test_datetime64.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
date_range,
2828
)
2929
import pandas._testing as tm
30-
from pandas.core.indexes.datetimes import _to_M8
3130
from pandas.core.ops import roperator
3231
from pandas.tests.arithmetic.common import (
3332
assert_invalid_addsub_type,
@@ -341,7 +340,7 @@ class TestDatetimeIndexComparisons:
341340
def test_comparators(self, op):
342341
index = tm.makeDateIndex(100)
343342
element = index[len(index) // 2]
344-
element = _to_M8(element)
343+
element = Timestamp(element).to_datetime64()
345344

346345
arr = np.array(index)
347346
arr_result = op(arr, element)

pandas/tests/indexes/datetimes/test_indexing.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -434,9 +434,9 @@ def test_insert(self):
434434

435435
# see gh-7299
436436
idx = date_range("1/1/2000", periods=3, freq="D", tz="Asia/Tokyo", name="idx")
437-
with pytest.raises(ValueError):
437+
with pytest.raises(TypeError, match="Cannot compare tz-naive and tz-aware"):
438438
idx.insert(3, pd.Timestamp("2000-01-04"))
439-
with pytest.raises(ValueError):
439+
with pytest.raises(TypeError, match="Cannot compare tz-naive and tz-aware"):
440440
idx.insert(3, datetime(2000, 1, 4))
441441
with pytest.raises(ValueError):
442442
idx.insert(3, pd.Timestamp("2000-01-04", tz="US/Eastern"))

pandas/tests/indexing/test_coercion.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -432,13 +432,19 @@ def test_insert_index_datetimes(self, fill_val, exp_dtype):
432432
)
433433
self._assert_insert_conversion(obj, fill_val, exp, exp_dtype)
434434

435-
msg = "Passed item and index have different timezone"
436435
if fill_val.tz:
437-
with pytest.raises(ValueError, match=msg):
436+
msg = "Cannot compare tz-naive and tz-aware"
437+
with pytest.raises(TypeError, match=msg):
438438
obj.insert(1, pd.Timestamp("2012-01-01"))
439439

440-
with pytest.raises(ValueError, match=msg):
441-
obj.insert(1, pd.Timestamp("2012-01-01", tz="Asia/Tokyo"))
440+
msg = "Timezones don't match"
441+
with pytest.raises(ValueError, match=msg):
442+
obj.insert(1, pd.Timestamp("2012-01-01", tz="Asia/Tokyo"))
443+
444+
else:
445+
msg = "Cannot compare tz-naive and tz-aware"
446+
with pytest.raises(TypeError, match=msg):
447+
obj.insert(1, pd.Timestamp("2012-01-01", tz="Asia/Tokyo"))
442448

443449
msg = "cannot insert DatetimeIndex with incompatible label"
444450
with pytest.raises(TypeError, match=msg):

0 commit comments

Comments
 (0)