8000 REF: use shareable code for DTI/TDI.insert (#30806) · pandas-dev/pandas@a73ce98 · GitHub
[go: up one dir, main page]

Skip to content

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

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)
0