diff --git a/doc/source/whatsnew/v0.17.0.txt b/doc/source/whatsnew/v0.17.0.txt index 18c39ccf820eb..3af9c887bb82f 100644 --- a/doc/source/whatsnew/v0.17.0.txt +++ b/doc/source/whatsnew/v0.17.0.txt @@ -629,6 +629,7 @@ Bug Fixes - Bug that caused segfault when resampling an empty Series (:issue:`10228`) - Bug in ``DatetimeIndex`` and ``PeriodIndex.value_counts`` resets name from its result, but retains in result's ``Index``. (:issue:`10150`) - Bug in ``pd.eval`` using ``numexpr`` engine coerces 1 element numpy array to scalar (:issue:`10546`) +- Bug in ``PeriodIndex.__contains__`` & ``DatetimeIndex.__contains__`` that always returned False for each other's objects (:issue:`10798`) - Bug in ``pd.concat`` with ``axis=0`` when column is of dtype ``category`` (:issue:`10177`) - Bug in ``read_msgpack`` where input type is not always checked (:issue:`10369`, :issue:`10630`) - Bug in ``pd.read_csv`` with kwargs ``index_col=False``, ``index_col=['a', 'b']`` or ``dtype`` diff --git a/pandas/tests/test_index.py b/pandas/tests/test_index.py index 9a3576a8fd846..52d2d85bda739 100644 --- a/pandas/tests/test_index.py +++ b/pandas/tests/test_index.py @@ -2817,7 +2817,31 @@ def test_view(self): result = self._holder(i) tm.assert_index_equal(result, i) -class TestDatetimeIndex(DatetimeLike, tm.TestCase): +class DatetimeAbsoluteLike(DatetimeLike): + + # GH10801 + def test_datetimeabsolute_contains(self): + + i = self.create_index() + + self.assertTrue(i[2] in i) + self.assertFalse('2012' in i) + + # python datetime objects + self.assertTrue(datetime(2013,1,1) in i) + + # strings + self.assertTrue('2013-1-1' in i) + + # Timestamp # GH10801 + self.assertTrue(pd.Timestamp('2013-1-1') in i) + + # pandas Period + self.assertTrue(pd.Period('2013-1-1', 'D') in i) + self.assertFalse(pd.Period('2013-1-1', 'M') in i) + + +class TestDatetimeIndex(DatetimeAbsoluteLike, tm.TestCase): _holder = DatetimeIndex _multiprocess_can_split_ = True @@ -2964,7 +2988,7 @@ def test_nat(self): self.assertIs(DatetimeIndex([np.nan])[0], pd.NaT) -class TestPeriodIndex(DatetimeLike, tm.TestCase): +class TestPeriodIndex(DatetimeAbsoluteLike, tm.TestCase): _holder = PeriodIndex _multiprocess_can_split_ = True diff --git a/pandas/tseries/index.py b/pandas/tseries/index.py index 19ff9a4b19a3e..8605058e3ec32 100644 --- a/pandas/tseries/index.py +++ b/pandas/tseries/index.py @@ -1289,6 +1289,12 @@ def get_loc(self, key, method=None): 'when key is a time object') return self.indexer_at_time(key) + # check if it's a Period and the frequencies are the same - otherwise a monthly period would match for + # a daily timestamp at the beginning of the month. NB: 'B' and 'D' therefore won't match + if isinstance(key, com.ABCPeriod) and key.freq == self.freq: + key = key.to_timestamp() + return Index.get_loc(self, key, method=method) + try: return Index.get_loc(self, key, method=method) except (KeyError, ValueError, TypeError): diff --git a/pandas/tseries/period.py b/pandas/tseries/period.py index e7b229e91cbc8..62289219ad72e 100644 --- a/pandas/tseries/period.py +++ b/pandas/tseries/period.py @@ -296,14 +296,14 @@ def _na_value(self): return self._box_func(tslib.iNaT) def __contains__(self, key): + # if key isn't a Period of the same freq, rely on `get_loc` for the coercion. if not isinstance(key, Period) or key.freq != self.freq: - if isinstance(key, compat.string_types): - try: - self.get_loc(key) - return True - except Exception: - return False - return False + try: + self.get_loc(key) + return True + except Exception: + return False + # If it is a Period of the same freq, go straight to the _engine return key.ordinal in self._engine @property