Repository: pandas-dev/pandas

Issue:
BUG: DatetimeIndex allows indexing with Timedelta and likewise for TimedeltaIndex/Timestamp
#### Code Sample, a copy-pastable example if possible
A `Series` or `DataFrame` with `DatetimeIndex` can currently be indexed with a `Timedelta`:
```python-traceback
In [2]: s = pd.Series(list('abc'), pd.date_range(0, periods=3))

In [3]: s
Out[3]:
1970-01-01    a
1970-01-02    b
1970-01-03    c
Freq: D, dtype: object

In [4]: s.loc[pd.Timedelta(0)]
Out[4]: 'a'

In [5]: s.loc[pd.Timedelta('0 days'):pd.Timedelta('1 day')]
Out[5]:
1970-01-01    a
1970-01-02    b
Freq: D, dtype: object
```

Likewise for `TimedeltaIndex` and `Timestamp`:
```python-traceback
In [6]: s = pd.Series(list('abc'), pd.timedelta_range(0, periods=3))

In [7]: s
Out[7]:
0 days    a
1 days    b
2 days    c
Freq: D, dtype: object

In [8]: s.loc[pd.Timestamp(0)]
Out[8]: 'a'

In [9]: s.loc[pd.Timestamp('1970-01-01'):pd.Timestamp('1970-01-02')]
Out[9]:
0 days    a
1 days    b
Freq: D, dtype: object
```
#### Problem description
Matches are being returned for labels that are not in the index.

Similar to #20432, but doesn't seem as widespread.

#### Expected Output
I'd expect a `KeyError` to be raised.

#### Output of ``pd.show_versions()``

<details>

INSTALLED VERSIONS
------------------
commit: 85817a72897be808fb6d311f65fc6031e0216603
python: 3.6.1.final.0
python-bits: 64
OS: Windows
OS-release: 10
machine: AMD64
processor: Intel64 Family 6 Model 78 Stepping 3, GenuineIntel
byteorder: little
LC_ALL: None
LANG: None
LOCALE: None.None

pandas: 0.23.0.dev0+667.g85817a7
pytest: 3.1.2
pip: 9.0.1
setuptools: 27.2.0
Cython: 0.25.2
numpy: 1.13.3
scipy: 1.0.0
pyarrow: 0.6.0
xarray: 0.9.6
IPython: 6.1.0
sphinx: 1.5.6
patsy: 0.4.1
dateutil: 2.6.0
pytz: 2017.2
blosc: None
bottleneck: None
tables: 3.4.2
numexpr: 2.6.4
feather: 0.4.0
matplotlib: 2.0.2
openpyxl: 2.4.8
xlrd: 1.1.0
xlwt: 1.3.0
xlsxwriter: 0.9.8
lxml: 3.8.0
bs4: None
html5lib: 0.999
sqlalchemy: 1.1.13
pymysql: None
psycopg2: None
jinja2: 2.9.6
s3fs: None
fastparquet: 0.1.0
pandas_gbq: None
pandas_datareader: None
</details>



Discussion:
these are being treated as integers (not correct)
Looks like this is specific to `pd.Timedelta(0)`, trying it with `pd.Timedelta(1)` raises correctly.

Resolving patch:
diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py
--- a/pandas/core/indexes/datetimes.py
+++ b/pandas/core/indexes/datetimes.py
@@ -2,7 +2,7 @@
 from __future__ import division
 import operator
 import warnings
-from datetime import time, datetime
+from datetime import time, datetime, timedelta
 
 import numpy as np
 from pytz import utc
@@ -730,6 +730,10 @@ def to_period(self, freq=None):
         """
         from pandas.core.indexes.period import PeriodIndex
 
+        if self.tz is not None:
+            warnings.warn("Converting to PeriodIndex representation will "
+                          "drop timezone information.", UserWarning)
+
         if freq is None:
             freq = self.freqstr or self.inferred_freq
 
@@ -740,7 +744,7 @@ def to_period(self, freq=None):
 
             freq = get_period_alias(freq)
 
-        return PeriodIndex(self.values, name=self.name, freq=freq, tz=self.tz)
+        return PeriodIndex(self.values, name=self.name, freq=freq)
 
     def snap(self, freq='S'):
         """
@@ -1204,6 +1208,12 @@ def get_loc(self, key, method=None, tolerance=None):
             key = Timestamp(key, tz=self.tz)
             return Index.get_loc(self, key, method, tolerance)
 
+        elif isinstance(key, timedelta):
+            # GH#20464
+            raise TypeError("Cannot index {cls} with {other}"
+                            .format(cls=type(self).__name__,
+                                    other=type(key).__name__))
+
         if isinstance(key, time):
             if method is not None:
                 raise NotImplementedError('cannot yet lookup inexact labels '
diff --git a/pandas/core/indexes/timedeltas.py b/pandas/core/indexes/timedeltas.py
--- a/pandas/core/indexes/timedeltas.py
+++ b/pandas/core/indexes/timedeltas.py
@@ -1,5 +1,6 @@
 """ implement the TimedeltaIndex """
 import operator
+from datetime import datetime
 
 import numpy as np
 from pandas.core.dtypes.common import (
@@ -487,7 +488,11 @@ def get_loc(self, key, method=None, tolerance=None):
         -------
         loc : int
         """
-        if is_list_like(key):
+        if is_list_like(key) or (isinstance(key, datetime) and key is not NaT):
+            # GH#20464 datetime check here is to ensure we don't allow
+            #   datetime objects to be incorrectly treated as timedelta
+            #   objects; NaT is a special case because it plays a double role
+            #   as Not-A-Timedelta
             raise TypeError
 
         if isna(key):


Test patch:
diff --git a/pandas/tests/indexes/datetimes/test_astype.py b/pandas/tests/indexes/datetimes/test_astype.py
--- a/pandas/tests/indexes/datetimes/test_astype.py
+++ b/pandas/tests/indexes/datetimes/test_astype.py
@@ -246,7 +246,9 @@ def setup_method(self, method):
     def test_to_period_millisecond(self):
         index = self.index
 
-        period = index.to_period(freq='L')
+        with tm.assert_produces_warning(UserWarning):
+            # warning that timezone info will be lost
+            period = index.to_period(freq='L')
         assert 2 == len(period)
         assert period[0] == Period('2007-01-01 10:11:12.123Z', 'L')
         assert period[1] == Period('2007-01-01 10:11:13.789Z', 'L')
@@ -254,7 +256,9 @@ def test_to_period_millisecond(self):
     def test_to_period_microsecond(self):
         index = self.index
 
-        period = index.to_period(freq='U')
+        with tm.assert_produces_warning(UserWarning):
+            # warning that timezone info will be lost
+            period = index.to_period(freq='U')
         assert 2 == len(period)
         assert period[0] == Period('2007-01-01 10:11:12.123456Z', 'U')
         assert period[1] == Period('2007-01-01 10:11:13.789123Z', 'U')
@@ -264,12 +268,20 @@ def test_to_period_microsecond(self):
         dateutil.tz.tzutc()])
     def test_to_period_tz(self, tz):
         ts = date_range('1/1/2000', '2/1/2000', tz=tz)
-        result = ts.to_period()[0]
-        expected = ts[0].to_period()
+
+        with tm.assert_produces_warning(UserWarning):
+            # GH#21333 warning that timezone info will be lost
+            result = ts.to_period()[0]
+            expected = ts[0].to_period()
+
         assert result == expected
 
         expected = date_range('1/1/2000', '2/1/2000').to_period()
-        result = ts.to_period()
+
+        with tm.assert_produces_warning(UserWarning):
+            # GH#21333 warning that timezone info will be lost
+            result = ts.to_period()
+
         tm.assert_index_equal(result, expected)
 
     def test_to_period_nofreq(self):
diff --git a/pandas/tests/indexes/datetimes/test_indexing.py b/pandas/tests/indexes/datetimes/test_indexing.py
--- a/pandas/tests/indexes/datetimes/test_indexing.py
+++ b/pandas/tests/indexes/datetimes/test_indexing.py
@@ -586,3 +586,17 @@ def test_reasonable_keyerror(self):
         with pytest.raises(KeyError) as excinfo:
             index.get_loc('1/1/2000')
         assert '2000' in str(excinfo.value)
+
+    @pytest.mark.parametrize('key', [pd.Timedelta(0),
+                                     pd.Timedelta(1),
+                                     timedelta(0)])
+    def test_timedelta_invalid_key(self, key):
+        # GH#20464
+        dti = pd.date_range('1970-01-01', periods=10)
+        with pytest.raises(TypeError):
+            dti.get_loc(key)
+
+    def test_get_loc_nat(self):
+        # GH#20464
+        index = DatetimeIndex(['1/3/2000', 'NaT'])
+        assert index.get_loc(pd.NaT) == 1
diff --git a/pandas/tests/indexes/timedeltas/test_indexing.py b/pandas/tests/indexes/timedeltas/test_indexing.py
--- a/pandas/tests/indexes/timedeltas/test_indexing.py
+++ b/pandas/tests/indexes/timedeltas/test_indexing.py
@@ -1,4 +1,4 @@
-from datetime import timedelta
+from datetime import datetime, timedelta
 
 import pytest
 import numpy as np
@@ -41,6 +41,15 @@ def test_getitem(self):
             tm.assert_index_equal(result, expected)
             assert result.freq == expected.freq
 
+    @pytest.mark.parametrize('key', [pd.Timestamp('1970-01-01'),
+                                     pd.Timestamp('1970-01-02'),
+                                     datetime(1970, 1, 1)])
+    def test_timestamp_invalid_key(self, key):
+        # GH#20464
+        tdi = pd.timedelta_range(0, periods=10)
+        with pytest.raises(TypeError):
+            tdi.get_loc(key)
+
 
 class TestWhere(object):
     # placeholder for symmetry with DatetimeIndex and PeriodIndex tests
diff --git a/pandas/tests/scalar/timestamp/test_timestamp.py b/pandas/tests/scalar/timestamp/test_timestamp.py
--- a/pandas/tests/scalar/timestamp/test_timestamp.py
+++ b/pandas/tests/scalar/timestamp/test_timestamp.py
@@ -929,3 +929,11 @@ def test_to_datetime_bijective(self):
         with tm.assert_produces_warning(exp_warning, check_stacklevel=False):
             assert (Timestamp(Timestamp.min.to_pydatetime()).value / 1000 ==
                     Timestamp.min.value / 1000)
+
+    def test_to_period_tz_warning(self):
+        # GH#21333 make sure a warning is issued when timezone
+        # info is lost
+        ts = Timestamp('2009-04-15 16:17:18', tz='US/Eastern')
+        with tm.assert_produces_warning(UserWarning):
+            # warning that timezone info will be lost
+            ts.to_period('D')