From 8fb1a0dc6893de9c914e1b56fa4989fe78918960 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Sun, 17 Dec 2017 20:45:42 -0800 Subject: [PATCH 1/8] delegate (most) datetimelike Series arithmetic ops to DatetimeIndex --- pandas/core/indexes/datetimelike.py | 15 ++++- pandas/core/ops.py | 56 +++++++++++++++---- .../indexes/datetimes/test_arithmetic.py | 14 +++++ pandas/tests/series/test_operators.py | 19 ++++++- pandas/tests/series/test_timeseries.py | 2 +- 5 files changed, 92 insertions(+), 14 deletions(-) diff --git a/pandas/core/indexes/datetimelike.py b/pandas/core/indexes/datetimelike.py index 8cc996285fbbd..ea3d20c69e3ad 100644 --- a/pandas/core/indexes/datetimelike.py +++ b/pandas/core/indexes/datetimelike.py @@ -11,7 +11,7 @@ import numpy as np from pandas.core.dtypes.common import ( - is_integer, is_float, + is_integer, is_float, is_integer_dtype, is_bool_dtype, _ensure_int64, is_scalar, is_dtype_equal, is_list_like, is_timedelta64_dtype) @@ -650,6 +650,7 @@ def _add_datetimelike_methods(cls): def __add__(self, other): from pandas.core.index import Index from pandas.core.indexes.timedeltas import TimedeltaIndex + from pandas.core.indexes.datetimes import DatetimeIndex from pandas.tseries.offsets import DateOffset if is_timedelta64_dtype(other): return self._add_delta(other) @@ -664,6 +665,12 @@ def __add__(self, other): return self.shift(other) elif isinstance(other, (Index, datetime, np.datetime64)): return self._add_datelike(other) + elif (isinstance(self, DatetimeIndex) and + isinstance(other, np.ndarray) and other.size == 1 + and is_integer_dtype(other)): + # TODO: Should this be allowed if self.freq is not None? + raise TypeError("cannot add {cls} and {typ}" + .format(cls=type(cls), typ=type(other))) else: # pragma: no cover return NotImplemented cls.__add__ = __add__ @@ -695,6 +702,12 @@ def __sub__(self, other): return self._sub_datelike(other) elif isinstance(other, Period): return self._sub_period(other) + elif (isinstance(self, DatetimeIndex) and + isinstance(other, np.ndarray) and other.size == 1 + and is_integer_dtype(other)): + # TODO: Should this be allowed if self.freq is not None? + raise TypeError("cannot add {cls} and {typ}" + .format(cls=type(cls), typ=type(other))) else: # pragma: no cover return NotImplemented cls.__sub__ = __sub__ diff --git a/pandas/core/ops.py b/pandas/core/ops.py index 2fb0cbb14c225..689f37bb876a8 100644 --- a/pandas/core/ops.py +++ b/pandas/core/ops.py @@ -363,7 +363,7 @@ def __init__(self, left, right, name, na_op): rvalues = self._convert_to_array(right, name=name, other=lvalues) # left - self.is_offset_lhs = self._is_offset(left) + self.is_offset_lhs = _is_offset(left) self.is_timedelta_lhs = is_timedelta64_dtype(lvalues) self.is_datetime64_lhs = is_datetime64_dtype(lvalues) self.is_datetime64tz_lhs = is_datetime64tz_dtype(lvalues) @@ -373,7 +373,7 @@ def __init__(self, left, right, name, na_op): self.is_floating_lhs = left.dtype.kind == 'f' # right - self.is_offset_rhs = self._is_offset(right) + self.is_offset_rhs = _is_offset(right) self.is_datetime64_rhs = is_datetime64_dtype(rvalues) self.is_datetime64tz_rhs = is_datetime64tz_dtype(rvalues) self.is_datetime_rhs = (self.is_datetime64_rhs or @@ -489,6 +489,7 @@ def _convert_to_array(self, values, name=None, other=None): # datetime with tz elif (isinstance(ovalues, datetime.datetime) and hasattr(ovalues, 'tzinfo')): + # TODO: does this mean to say `ovalues.tzinfo is not None`? values = pd.DatetimeIndex(values) # datetime array with tz elif is_datetimetz(values): @@ -515,7 +516,7 @@ def _convert_to_array(self, values, name=None, other=None): values = np.empty(values.shape, dtype=other.dtype) values[:] = iNaT return values - elif self._is_offset(values): + elif _is_offset(values): return values else: raise TypeError("incompatible type [{dtype}] for a " @@ -618,14 +619,15 @@ def f(x): return lvalues, rvalues - def _is_offset(self, arr_or_obj): - """ check if obj or all elements of list-like is DateOffset """ - if isinstance(arr_or_obj, ABCDateOffset): - return True - elif (is_list_like(arr_or_obj) and len(arr_or_obj) and - is_object_dtype(arr_or_obj)): - return all(isinstance(x, ABCDateOffset) for x in arr_or_obj) - return False + +def _is_offset(arr_or_obj): + """ check if obj or all elements of list-like is DateOffset """ + if isinstance(arr_or_obj, ABCDateOffset): + return True + elif (is_list_like(arr_or_obj) and len(arr_or_obj) and + is_object_dtype(arr_or_obj)): + return all(isinstance(x, ABCDateOffset) for x in arr_or_obj) + return False def _align_method_SERIES(left, right, align_asobject=False): @@ -663,6 +665,15 @@ def _construct_divmod_result(left, result, index, name, dtype): ) +def _get_series_result_name(left, rvalues): + # TODO: Can we just use right instead of rvalues? + if isinstance(rvalues, ABCSeries): + name = _maybe_match_name(left, rvalues) + else: + name = left.name + return name + + def _arith_method_SERIES(op, name, str_rep, fill_zeros=None, default_axis=None, construct_result=_construct_result, **eval_kwargs): """ @@ -715,6 +726,29 @@ def wrapper(left, right, name=name, na_op=na_op): if isinstance(right, ABCDataFrame): return NotImplemented + if _is_offset(right): + # special handling for alignment + pass + elif isinstance(right, pd.PeriodIndex): + # not supported for DatetimeIndex + pass + elif is_datetime64_dtype(left) or is_datetime64tz_dtype(left): + # Dispatch to DatetimeIndex method + if right is pd.NaT: + # DatetimeIndex and Series handle this differently, so + # until that is resolved we need to special-case here + return construct_result(left, pd.NaT, index=left.index, + name=left.name, dtype=left.dtype) + # TODO: double-check that the tz part of the dtype + # is supposed to be retained + left, right = _align_method_SERIES(left, right) + name = _get_series_result_name(left, right) + result = op(pd.DatetimeIndex(left), right) + result.name = name # Needs to be overriden if name is None + return construct_result(left, result, + index=left.index, name=name, + dtype=result.dtype) + left, right = _align_method_SERIES(left, right) converted = _Op.get_op(left, right, name, na_op) diff --git a/pandas/tests/indexes/datetimes/test_arithmetic.py b/pandas/tests/indexes/datetimes/test_arithmetic.py index a46462e91a866..3b612ef3e1f89 100644 --- a/pandas/tests/indexes/datetimes/test_arithmetic.py +++ b/pandas/tests/indexes/datetimes/test_arithmetic.py @@ -363,6 +363,20 @@ def test_datetimeindex_sub_timestamp_overflow(self): with pytest.raises(OverflowError): dtimin - variant + def test_dti_add_intarray(self, tz): + rng = pd.date_range('2000-01-01 09:00', freq='H', + periods=10, tz=tz) + other = np.array(1, dtype=np.int64) + with pytest.raises(TypeError): + rng + other + + def test_dti_sub_intarray(self, tz): + rng = pd.date_range('2000-01-01 09:00', freq='H', + periods=10, tz=tz) + other = np.array(1, dtype=np.int64) + with pytest.raises(TypeError): + rng - other + # GH 10699 @pytest.mark.parametrize('klass,assert_func', zip([Series, DatetimeIndex], diff --git a/pandas/tests/series/test_operators.py b/pandas/tests/series/test_operators.py index 6cc866a35514f..9fc30d3c77164 100644 --- a/pandas/tests/series/test_operators.py +++ b/pandas/tests/series/test_operators.py @@ -28,6 +28,23 @@ from .common import TestData +class TestDatetimeLikeArithmetic(object): + def test_datetime_sub_datetime_overflow(self): + # GH#12534 + dt = pd.Timestamp('1700-01-31') + dti = pd.date_range('1999-09-30', freq='M', periods=10) + with pytest.raises(OverflowError): + dti - dt + with pytest.raises(OverflowError): + dt - dti + + ser = pd.Series(dti) + with pytest.raises(OverflowError): + ser - dt + with pytest.raises(OverflowError): + dt - ser + + class TestSeriesOperators(TestData): def test_series_comparison_scalars(self): @@ -612,7 +629,7 @@ def run_ops(ops, get_ser, test_ser): # defined for op_str in ops: op = getattr(get_ser, op_str, None) - with tm.assert_raises_regex(TypeError, 'operate'): + with tm.assert_raises_regex(TypeError, 'operate|cannot'): op(test_ser) # ## timedelta64 ### diff --git a/pandas/tests/series/test_timeseries.py b/pandas/tests/series/test_timeseries.py index 95410c6ea0105..475b6ace9f7a5 100644 --- a/pandas/tests/series/test_timeseries.py +++ b/pandas/tests/series/test_timeseries.py @@ -107,7 +107,7 @@ def test_shift(self): # incompat tz s2 = Series(date_range('2000-01-01 09:00:00', periods=5, tz='CET'), name='foo') - pytest.raises(ValueError, lambda: s - s2) + pytest.raises(TypeError, lambda: s - s2) def test_shift2(self): ts = Series(np.random.randn(5), From 6d7c6fbaf9a5e0871c3738171c582ad421109f9a Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Sun, 17 Dec 2017 20:53:05 -0800 Subject: [PATCH 2/8] flake8 fixup --- pandas/core/indexes/datetimelike.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pandas/core/indexes/datetimelike.py b/pandas/core/indexes/datetimelike.py index ea3d20c69e3ad..52ad95ae9ab0d 100644 --- a/pandas/core/indexes/datetimelike.py +++ b/pandas/core/indexes/datetimelike.py @@ -666,8 +666,8 @@ def __add__(self, other): elif isinstance(other, (Index, datetime, np.datetime64)): return self._add_datelike(other) elif (isinstance(self, DatetimeIndex) and - isinstance(other, np.ndarray) and other.size == 1 - and is_integer_dtype(other)): + isinstance(other, np.ndarray) and other.size == 1 and + is_integer_dtype(other)): # TODO: Should this be allowed if self.freq is not None? raise TypeError("cannot add {cls} and {typ}" .format(cls=type(cls), typ=type(other))) @@ -703,8 +703,8 @@ def __sub__(self, other): elif isinstance(other, Period): return self._sub_period(other) elif (isinstance(self, DatetimeIndex) and - isinstance(other, np.ndarray) and other.size == 1 - and is_integer_dtype(other)): + isinstance(other, np.ndarray) and other.size == 1 and + is_integer_dtype(other)): # TODO: Should this be allowed if self.freq is not None? raise TypeError("cannot add {cls} and {typ}" .format(cls=type(cls), typ=type(other))) From d86af39322c804c9476ffecb55762d4c1a74491a Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Mon, 18 Dec 2017 07:48:07 -0800 Subject: [PATCH 3/8] move+deprivatize+test is_offset --- pandas/core/dtypes/common.py | 14 ++++++++++++-- pandas/core/ops.py | 23 ++++++----------------- pandas/tests/dtypes/test_common.py | 14 ++++++++++++++ 3 files changed, 32 insertions(+), 19 deletions(-) diff --git a/pandas/core/dtypes/common.py b/pandas/core/dtypes/common.py index 5b1335c1a834e..5fdc76fe7a4d7 100644 --- a/pandas/core/dtypes/common.py +++ b/pandas/core/dtypes/common.py @@ -12,8 +12,8 @@ from .generic import (ABCCategorical, ABCPeriodIndex, ABCDatetimeIndex, ABCSeries, ABCSparseArray, ABCSparseSeries, ABCCategoricalIndex, - ABCIndexClass) -from .inference import is_string_like + ABCIndexClass, ABCDateOffset) +from .inference import is_string_like, is_list_like from .inference import * # noqa @@ -266,6 +266,16 @@ def is_datetimetz(arr): is_datetime64tz_dtype(arr)) +def is_offsetlike(arr_or_obj): + """ check if obj or all elements of list-like is DateOffset """ + if isinstance(arr_or_obj, ABCDateOffset): + return True + elif (is_list_like(arr_or_obj) and len(arr_or_obj) and + is_object_dtype(arr_or_obj)): + return all(isinstance(x, ABCDateOffset) for x in arr_or_obj) + return False + + def is_period(arr): """ Check whether an array-like is a periodical index. diff --git a/pandas/core/ops.py b/pandas/core/ops.py index 689f37bb876a8..d90a367599e73 100644 --- a/pandas/core/ops.py +++ b/pandas/core/ops.py @@ -30,7 +30,7 @@ is_object_dtype, is_timedelta64_dtype, is_datetime64_dtype, is_datetime64tz_dtype, is_bool_dtype, is_datetimetz, - is_list_like, + is_list_like, is_offsetlike, is_scalar, _ensure_object) from pandas.core.dtypes.cast import maybe_upcast_putmask, find_common_type @@ -38,8 +38,7 @@ ABCSeries, ABCDataFrame, ABCIndex, - ABCPeriodIndex, - ABCDateOffset) + ABCPeriodIndex) # ----------------------------------------------------------------------------- # Functions that add arithmetic methods to objects, given arithmetic factory @@ -363,7 +362,7 @@ def __init__(self, left, right, name, na_op): rvalues = self._convert_to_array(right, name=name, other=lvalues) # left - self.is_offset_lhs = _is_offset(left) + self.is_offset_lhs = is_offsetlike(left) self.is_timedelta_lhs = is_timedelta64_dtype(lvalues) self.is_datetime64_lhs = is_datetime64_dtype(lvalues) self.is_datetime64tz_lhs = is_datetime64tz_dtype(lvalues) @@ -373,7 +372,7 @@ def __init__(self, left, right, name, na_op): self.is_floating_lhs = left.dtype.kind == 'f' # right - self.is_offset_rhs = _is_offset(right) + self.is_offset_rhs = is_offsetlike(right) self.is_datetime64_rhs = is_datetime64_dtype(rvalues) self.is_datetime64tz_rhs = is_datetime64tz_dtype(rvalues) self.is_datetime_rhs = (self.is_datetime64_rhs or @@ -516,7 +515,7 @@ def _convert_to_array(self, values, name=None, other=None): values = np.empty(values.shape, dtype=other.dtype) values[:] = iNaT return values - elif _is_offset(values): + elif is_offsetlike(values): return values else: raise TypeError("incompatible type [{dtype}] for a " @@ -620,16 +619,6 @@ def f(x): return lvalues, rvalues -def _is_offset(arr_or_obj): - """ check if obj or all elements of list-like is DateOffset """ - if isinstance(arr_or_obj, ABCDateOffset): - return True - elif (is_list_like(arr_or_obj) and len(arr_or_obj) and - is_object_dtype(arr_or_obj)): - return all(isinstance(x, ABCDateOffset) for x in arr_or_obj) - return False - - def _align_method_SERIES(left, right, align_asobject=False): """ align lhs and rhs Series """ @@ -726,7 +715,7 @@ def wrapper(left, right, name=name, na_op=na_op): if isinstance(right, ABCDataFrame): return NotImplemented - if _is_offset(right): + if is_offsetlike(right): # special handling for alignment pass elif isinstance(right, pd.PeriodIndex): diff --git a/pandas/tests/dtypes/test_common.py b/pandas/tests/dtypes/test_common.py index 2146704fea95f..9a18d2d30d0ee 100644 --- a/pandas/tests/dtypes/test_common.py +++ b/pandas/tests/dtypes/test_common.py @@ -537,6 +537,20 @@ def test_is_complex_dtype(): assert com.is_complex_dtype(np.array([1 + 1j, 5])) +def test_is_offsetlike(): + assert com.is_offsetlike(np.array([pd.DateOffset(month=3), + pd.offsets.Nano()])) + assert com.is_offsetlike(pd.offsets.MonthEnd()) + assert com.is_offsetlike([pd.offsets.MonthBegin(), + pd.offsets.YearBegin()]) + + assert not com.is_offsetlike(pd.Timedelta(1)) + assert not com.isoffsetlike(np.array([1 + 1j, 5])) + + # mixed case + assert not com.isoffsetlike([pd.DateOffset(), pd.Timestamp(0)]) + + @pytest.mark.parametrize('input_param,result', [ (int, np.dtype(int)), ('int32', np.dtype('int32')), From bbea697a39ee93229a338cdb39cd43e260934069 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Mon, 18 Dec 2017 09:49:19 -0800 Subject: [PATCH 4/8] fix test --- pandas/core/dtypes/common.py | 23 ++++++++++++++++++++++- pandas/tests/dtypes/test_common.py | 5 ++--- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/pandas/core/dtypes/common.py b/pandas/core/dtypes/common.py index 5fdc76fe7a4d7..e2ee3deb5396e 100644 --- a/pandas/core/dtypes/common.py +++ b/pandas/core/dtypes/common.py @@ -267,7 +267,28 @@ def is_datetimetz(arr): def is_offsetlike(arr_or_obj): - """ check if obj or all elements of list-like is DateOffset """ + """ + Check if obj or all elements of list-like is DateOffset + + Parameters + ---------- + arr_or_obj : object + + Returns + ------- + boolean : Whether the object is a DateOffset or listlike of DatetOffsets + + Examples + -------- + >>> is_offsetlike(pd.DateOffset(days=1)) + True + >>> is_offsetlike('offset') + False + >>> is_offsetlike([pd.offsets.Minute(4), pd.offsets.MonthEnd()]) + True + >>> is_offsetlike(np.array([pd.DateOffset(months=3), pd.Timestamp.now()])) + False + """ if isinstance(arr_or_obj, ABCDateOffset): return True elif (is_list_like(arr_or_obj) and len(arr_or_obj) and diff --git a/pandas/tests/dtypes/test_common.py b/pandas/tests/dtypes/test_common.py index 9a18d2d30d0ee..38eafaabc04e2 100644 --- a/pandas/tests/dtypes/test_common.py +++ b/pandas/tests/dtypes/test_common.py @@ -541,14 +541,13 @@ def test_is_offsetlike(): assert com.is_offsetlike(np.array([pd.DateOffset(month=3), pd.offsets.Nano()])) assert com.is_offsetlike(pd.offsets.MonthEnd()) - assert com.is_offsetlike([pd.offsets.MonthBegin(), - pd.offsets.YearBegin()]) + assert com.is_offsetlike(pd.Index([pd.DateOffset(second=1)])) assert not com.is_offsetlike(pd.Timedelta(1)) assert not com.isoffsetlike(np.array([1 + 1j, 5])) # mixed case - assert not com.isoffsetlike([pd.DateOffset(), pd.Timestamp(0)]) + assert not com.isoffsetlike(np.array([pd.DateOffset(), pd.Timestamp(0)])) @pytest.mark.parametrize('input_param,result', [ From 99e6cc6d6fb7e4abe862ab3197720e85babe41ed Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Mon, 18 Dec 2017 12:27:25 -0800 Subject: [PATCH 5/8] fixup typo --- pandas/tests/dtypes/test_common.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/tests/dtypes/test_common.py b/pandas/tests/dtypes/test_common.py index 38eafaabc04e2..bfec229d32b22 100644 --- a/pandas/tests/dtypes/test_common.py +++ b/pandas/tests/dtypes/test_common.py @@ -544,10 +544,10 @@ def test_is_offsetlike(): assert com.is_offsetlike(pd.Index([pd.DateOffset(second=1)])) assert not com.is_offsetlike(pd.Timedelta(1)) - assert not com.isoffsetlike(np.array([1 + 1j, 5])) + assert not com.is_offsetlike(np.array([1 + 1j, 5])) # mixed case - assert not com.isoffsetlike(np.array([pd.DateOffset(), pd.Timestamp(0)])) + assert not com.is_offsetlike(np.array([pd.DateOffset(), pd.Timestamp(0)])) @pytest.mark.parametrize('input_param,result', [ From 61f22ba51ab4d95328463f4b1472675d9c264f67 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Mon, 18 Dec 2017 15:04:11 -0800 Subject: [PATCH 6/8] Whatsnew note about change in exception class --- doc/source/whatsnew/v0.22.0.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/whatsnew/v0.22.0.txt b/doc/source/whatsnew/v0.22.0.txt index dfd222f10d235..96169fcb51aef 100644 --- a/doc/source/whatsnew/v0.22.0.txt +++ b/doc/source/whatsnew/v0.22.0.txt @@ -194,6 +194,7 @@ Other API Changes - Rearranged the order of keyword arguments in :func:`read_excel()` to align with :func:`read_csv()` (:issue:`16672`) - :func:`pandas.merge` now raises a ``ValueError`` when trying to merge on incompatible data types (:issue:`9780`) - :func:`wide_to_long` previously kept numeric-like suffixes as ``object`` dtype. Now they are cast to numeric if possible (:issue:`17627`) +- Subtraction of :class:`DatetimeIndex` with mis-matched timezones will now raise a ``TypeError`` instead of a ``ValueError`` (:issue:`18817`) .. _whatsnew_0220.deprecations: From cf677a17b856e9b96853a623a636e61ea6fcd088 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Mon, 18 Dec 2017 17:34:23 -0800 Subject: [PATCH 7/8] move special case checking inside the is_datetime check --- pandas/core/ops.py | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/pandas/core/ops.py b/pandas/core/ops.py index d90a367599e73..4ae87ab754efc 100644 --- a/pandas/core/ops.py +++ b/pandas/core/ops.py @@ -715,14 +715,10 @@ def wrapper(left, right, name=name, na_op=na_op): if isinstance(right, ABCDataFrame): return NotImplemented - if is_offsetlike(right): - # special handling for alignment - pass - elif isinstance(right, pd.PeriodIndex): - # not supported for DatetimeIndex - pass elif is_datetime64_dtype(left) or is_datetime64tz_dtype(left): - # Dispatch to DatetimeIndex method + # Dispatch to DatetimeIndex method; there are a handful of cases + # that DatetimeIndex handles differently from Series so we avoid + # dispatching. if right is pd.NaT: # DatetimeIndex and Series handle this differently, so # until that is resolved we need to special-case here @@ -730,13 +726,24 @@ def wrapper(left, right, name=name, na_op=na_op): name=left.name, dtype=left.dtype) # TODO: double-check that the tz part of the dtype # is supposed to be retained - left, right = _align_method_SERIES(left, right) - name = _get_series_result_name(left, right) - result = op(pd.DatetimeIndex(left), right) - result.name = name # Needs to be overriden if name is None - return construct_result(left, result, - index=left.index, name=name, - dtype=result.dtype) + elif is_offsetlike(right): + # special handling for alignment + pass + elif isinstance(right, pd.PeriodIndex): + # not supported for DatetimeIndex + pass + elif (isinstance(other, np.ndarray) and other.size == 1 and + is_integer_dtype(other)): + # DatetimeIndex adds this as nanoseconds, needs fixing + pass + else: + left, right = _align_method_SERIES(left, right) + name = _get_series_result_name(left, right) + result = op(pd.DatetimeIndex(left), right) + result.name = name # Needs to be overriden if name is None + return construct_result(left, result, + index=left.index, name=name, + dtype=result.dtype) left, right = _align_method_SERIES(left, right) From ab7501822da0e81424b3568588f0aedff804baf9 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Tue, 19 Dec 2017 08:11:35 -0800 Subject: [PATCH 8/8] fixup name typo --- pandas/core/ops.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/core/ops.py b/pandas/core/ops.py index 49442f0e545ac..30171e3b204ac 100644 --- a/pandas/core/ops.py +++ b/pandas/core/ops.py @@ -734,8 +734,8 @@ def wrapper(left, right, name=name, na_op=na_op): elif isinstance(right, pd.PeriodIndex): # not supported for DatetimeIndex pass - elif (isinstance(other, np.ndarray) and other.size == 1 and - is_integer_dtype(other)): + elif (isinstance(right, np.ndarray) and right.size == 1 and + is_integer_dtype(right)): # DatetimeIndex adds this as nanoseconds, needs fixing pass else: