Repository: pandas-dev/pandas

Issue:
API: NaT/nan mixed input coerces to timedelta64
#### Code Sample, a copy-pastable example if possible

`Index`/`Series` results in `datetime64` dtype if all inputs are `pd.NaT`.

```
pd.Index([pd.NaT, pd.NaT])
# DatetimeIndex(['NaT', 'NaT'], dtype='datetime64[ns]', freq=None)

pd.Series([pd.NaT, pd.NaT])
#0   NaT
#1   NaT
# dtype: datetime64[ns]
```

However, it coerces to `timedelta64` if `np.nan` is included. 

```
pd.Index([pd.NaT, np.nan])
# TimedeltaIndex([NaT, NaT], dtype='timedelta64[ns]', freq=None)

pd.Series([pd.NaT, np.nan])
#0   NaT
#1   NaT
# dtype: timedelta64[ns]
```

This looks to be caused by following internals:

```
pd.tslib.is_timestamp_array(np.array([pd.NaT, np.nan]))
# False
pd.lib.is_timedelta_array(np.array([pd.NaT, np.nan]))
# True
```
#### Expected Output

`datetime64` dtype?
#### output of `pd.show_versions()`

on current master



Discussion:
`tslib.is_timestamp_array` looks totally out of place / is old, need to remove that and replace with routine from `src/inference.pyx` (which is where `is_timedelta_array` is defined, along with all other inference routines); these get put into `lib.pyx` at compile time.

but your result should be the same (e.g. `M8[ns]`)

OK. Also there is no tests to guarantee the behavior. Will submit a PR.


Resolving patch:
diff --git a/pandas/indexes/base.py b/pandas/indexes/base.py
--- a/pandas/indexes/base.py
+++ b/pandas/indexes/base.py
@@ -242,8 +242,7 @@ def __new__(cls, data=None, dtype=None, copy=False, name=None,
                     # don't support boolean explicity ATM
                     pass
                 elif inferred != 'string':
-                    if (inferred.startswith('datetime') or
-                            tslib.is_timestamp_array(subarr)):
+                    if inferred.startswith('datetime'):
 
                         if (lib.is_datetime_with_singletz_array(subarr) or
                                 'tz' in kwargs):


Test patch:
diff --git a/pandas/tests/indexes/test_base.py b/pandas/tests/indexes/test_base.py
--- a/pandas/tests/indexes/test_base.py
+++ b/pandas/tests/indexes/test_base.py
@@ -203,6 +203,49 @@ def __array__(self, dtype=None):
             result = pd.Index(ArrayLike(array))
             self.assert_index_equal(result, expected)
 
+    def test_index_ctor_infer_nan_nat(self):
+        # GH 13467
+        exp = pd.Float64Index([np.nan, np.nan])
+        self.assertEqual(exp.dtype, np.float64)
+        tm.assert_index_equal(Index([np.nan, np.nan]), exp)
+        tm.assert_index_equal(Index(np.array([np.nan, np.nan])), exp)
+
+        exp = pd.DatetimeIndex([pd.NaT, pd.NaT])
+        self.assertEqual(exp.dtype, 'datetime64[ns]')
+        tm.assert_index_equal(Index([pd.NaT, pd.NaT]), exp)
+        tm.assert_index_equal(Index(np.array([pd.NaT, pd.NaT])), exp)
+
+        exp = pd.DatetimeIndex([pd.NaT, pd.NaT])
+        self.assertEqual(exp.dtype, 'datetime64[ns]')
+
+        for data in [[pd.NaT, np.nan], [np.nan, pd.NaT],
+                     [np.nan, np.datetime64('nat')],
+                     [np.datetime64('nat'), np.nan]]:
+            tm.assert_index_equal(Index(data), exp)
+            tm.assert_index_equal(Index(np.array(data, dtype=object)), exp)
+
+        exp = pd.TimedeltaIndex([pd.NaT, pd.NaT])
+        self.assertEqual(exp.dtype, 'timedelta64[ns]')
+
+        for data in [[np.nan, np.timedelta64('nat')],
+                     [np.timedelta64('nat'), np.nan],
+                     [pd.NaT, np.timedelta64('nat')],
+                     [np.timedelta64('nat'), pd.NaT]]:
+
+            tm.assert_index_equal(Index(data), exp)
+            tm.assert_index_equal(Index(np.array(data, dtype=object)), exp)
+
+        # mixed np.datetime64/timedelta64 nat results in object
+        data = [np.datetime64('nat'), np.timedelta64('nat')]
+        exp = pd.Index(data, dtype=object)
+        tm.assert_index_equal(Index(data), exp)
+        tm.assert_index_equal(Index(np.array(data, dtype=object)), exp)
+
+        data = [np.timedelta64('nat'), np.datetime64('nat')]
+        exp = pd.Index(data, dtype=object)
+        tm.assert_index_equal(Index(data), exp)
+        tm.assert_index_equal(Index(np.array(data, dtype=object)), exp)
+
     def test_index_ctor_infer_periodindex(self):
         xp = period_range('2012-1-1', freq='M', periods=3)
         rs = Index(xp)
diff --git a/pandas/tests/series/test_constructors.py b/pandas/tests/series/test_constructors.py
--- a/pandas/tests/series/test_constructors.py
+++ b/pandas/tests/series/test_constructors.py
@@ -252,6 +252,24 @@ def test_constructor_pass_none(self):
         expected = Series(index=Index([None]))
         assert_series_equal(s, expected)
 
+    def test_constructor_pass_nan_nat(self):
+        # GH 13467
+        exp = Series([np.nan, np.nan], dtype=np.float64)
+        self.assertEqual(exp.dtype, np.float64)
+        tm.assert_series_equal(Series([np.nan, np.nan]), exp)
+        tm.assert_series_equal(Series(np.array([np.nan, np.nan])), exp)
+
+        exp = Series([pd.NaT, pd.NaT])
+        self.assertEqual(exp.dtype, 'datetime64[ns]')
+        tm.assert_series_equal(Series([pd.NaT, pd.NaT]), exp)
+        tm.assert_series_equal(Series(np.array([pd.NaT, pd.NaT])), exp)
+
+        tm.assert_series_equal(Series([pd.NaT, np.nan]), exp)
+        tm.assert_series_equal(Series(np.array([pd.NaT, np.nan])), exp)
+
+        tm.assert_series_equal(Series([np.nan, pd.NaT]), exp)
+        tm.assert_series_equal(Series(np.array([np.nan, pd.NaT])), exp)
+
     def test_constructor_cast(self):
         self.assertRaises(ValueError, Series, ['a', 'b', 'c'], dtype=float)
 
@@ -688,8 +706,9 @@ def test_constructor_dtype_timedelta64(self):
         td = Series([np.timedelta64(300000000), pd.NaT])
         self.assertEqual(td.dtype, 'timedelta64[ns]')
 
+        # because iNaT is int, not coerced to timedelta
         td = Series([np.timedelta64(300000000), tslib.iNaT])
-        self.assertEqual(td.dtype, 'timedelta64[ns]')
+        self.assertEqual(td.dtype, 'object')
 
         td = Series([np.timedelta64(300000000), np.nan])
         self.assertEqual(td.dtype, 'timedelta64[ns]')
diff --git a/pandas/tests/test_infer_and_convert.py b/pandas/tests/test_infer_and_convert.py
--- a/pandas/tests/test_infer_and_convert.py
+++ b/pandas/tests/test_infer_and_convert.py
@@ -180,6 +180,207 @@ def test_datetime(self):
         index = Index(dates)
         self.assertEqual(index.inferred_type, 'datetime64')
 
+    def test_infer_dtype_datetime(self):
+
+        arr = np.array([pd.Timestamp('2011-01-01'),
+                        pd.Timestamp('2011-01-02')])
+        self.assertEqual(pd.lib.infer_dtype(arr), 'datetime')
+
+        arr = np.array([np.datetime64('2011-01-01'),
+                        np.datetime64('2011-01-01')], dtype=object)
+        self.assertEqual(pd.lib.infer_dtype(arr), 'datetime64')
+
+        arr = np.array([datetime(2011, 1, 1), datetime(2012, 2, 1)])
+        self.assertEqual(pd.lib.infer_dtype(arr), 'datetime')
+
+        # starts with nan
+        for n in [pd.NaT, np.nan]:
+            arr = np.array([n, pd.Timestamp('2011-01-02')])
+            self.assertEqual(pd.lib.infer_dtype(arr), 'datetime')
+
+            arr = np.array([n, np.datetime64('2011-01-02')])
+            self.assertEqual(pd.lib.infer_dtype(arr), 'datetime64')
+
+            arr = np.array([n, datetime(2011, 1, 1)])
+            self.assertEqual(pd.lib.infer_dtype(arr), 'datetime')
+
+            arr = np.array([n, pd.Timestamp('2011-01-02'), n])
+            self.assertEqual(pd.lib.infer_dtype(arr), 'datetime')
+
+            arr = np.array([n, np.datetime64('2011-01-02'), n])
+            self.assertEqual(pd.lib.infer_dtype(arr), 'datetime64')
+
+            arr = np.array([n, datetime(2011, 1, 1), n])
+            self.assertEqual(pd.lib.infer_dtype(arr), 'datetime')
+
+        # different type of nat
+        arr = np.array([np.timedelta64('nat'),
+                        np.datetime64('2011-01-02')], dtype=object)
+        self.assertEqual(pd.lib.infer_dtype(arr), 'mixed')
+
+        arr = np.array([np.datetime64('2011-01-02'),
+                        np.timedelta64('nat')], dtype=object)
+        self.assertEqual(pd.lib.infer_dtype(arr), 'mixed')
+
+        # mixed datetime
+        arr = np.array([datetime(2011, 1, 1),
+                        pd.Timestamp('2011-01-02')])
+        self.assertEqual(pd.lib.infer_dtype(arr), 'datetime')
+
+        # should be datetime?
+        arr = np.array([np.datetime64('2011-01-01'),
+                        pd.Timestamp('2011-01-02')])
+        self.assertEqual(pd.lib.infer_dtype(arr), 'mixed')
+
+        arr = np.array([pd.Timestamp('2011-01-02'),
+                        np.datetime64('2011-01-01')])
+        self.assertEqual(pd.lib.infer_dtype(arr), 'mixed')
+
+        arr = np.array([np.nan, pd.Timestamp('2011-01-02'), 1])
+        self.assertEqual(pd.lib.infer_dtype(arr), 'mixed-integer')
+
+        arr = np.array([np.nan, pd.Timestamp('2011-01-02'), 1.1])
+        self.assertEqual(pd.lib.infer_dtype(arr), 'mixed')
+
+        arr = np.array([np.nan, '2011-01-01', pd.Timestamp('2011-01-02')])
+        self.assertEqual(pd.lib.infer_dtype(arr), 'mixed')
+
+    def test_infer_dtype_timedelta(self):
+
+        arr = np.array([pd.Timedelta('1 days'),
+                        pd.Timedelta('2 days')])
+        self.assertEqual(pd.lib.infer_dtype(arr), 'timedelta')
+
+        arr = np.array([np.timedelta64(1, 'D'),
+                        np.timedelta64(2, 'D')], dtype=object)
+        self.assertEqual(pd.lib.infer_dtype(arr), 'timedelta')
+
+        arr = np.array([timedelta(1), timedelta(2)])
+        self.assertEqual(pd.lib.infer_dtype(arr), 'timedelta')
+
+        # starts with nan
+        for n in [pd.NaT, np.nan]:
+            arr = np.array([n, pd.Timedelta('1 days')])
+            self.assertEqual(pd.lib.infer_dtype(arr), 'timedelta')
+
+            arr = np.array([n, np.timedelta64(1, 'D')])
+            self.assertEqual(pd.lib.infer_dtype(arr), 'timedelta')
+
+            arr = np.array([n, timedelta(1)])
+            self.assertEqual(pd.lib.infer_dtype(arr), 'timedelta')
+
+            arr = np.array([n, pd.Timedelta('1 days'), n])
+            self.assertEqual(pd.lib.infer_dtype(arr), 'timedelta')
+
+            arr = np.array([n, np.timedelta64(1, 'D'), n])
+            self.assertEqual(pd.lib.infer_dtype(arr), 'timedelta')
+
+            arr = np.array([n, timedelta(1), n])
+            self.assertEqual(pd.lib.infer_dtype(arr), 'timedelta')
+
+        # different type of nat
+        arr = np.array([np.datetime64('nat'), np.timedelta64(1, 'D')],
+                       dtype=object)
+        self.assertEqual(pd.lib.infer_dtype(arr), 'mixed')
+
+        arr = np.array([np.timedelta64(1, 'D'), np.datetime64('nat')],
+                       dtype=object)
+        self.assertEqual(pd.lib.infer_dtype(arr), 'mixed')
+
+    def test_infer_dtype_all_nan_nat_like(self):
+        arr = np.array([np.nan, np.nan])
+        self.assertEqual(pd.lib.infer_dtype(arr), 'floating')
+
+        # nan and None mix are result in mixed
+        arr = np.array([np.nan, np.nan, None])
+        self.assertEqual(pd.lib.infer_dtype(arr), 'mixed')
+
+        arr = np.array([None, np.nan, np.nan])
+        self.assertEqual(pd.lib.infer_dtype(arr), 'mixed')
+
+        # pd.NaT
+        arr = np.array([pd.NaT])
+        self.assertEqual(pd.lib.infer_dtype(arr), 'datetime')
+
+        arr = np.array([pd.NaT, np.nan])
+        self.assertEqual(pd.lib.infer_dtype(arr), 'datetime')
+
+        arr = np.array([np.nan, pd.NaT])
+        self.assertEqual(pd.lib.infer_dtype(arr), 'datetime')
+
+        arr = np.array([np.nan, pd.NaT, np.nan])
+        self.assertEqual(pd.lib.infer_dtype(arr), 'datetime')
+
+        arr = np.array([None, pd.NaT, None])
+        self.assertEqual(pd.lib.infer_dtype(arr), 'datetime')
+
+        # np.datetime64(nat)
+        arr = np.array([np.datetime64('nat')])
+        self.assertEqual(pd.lib.infer_dtype(arr), 'datetime64')
+
+        for n in [np.nan, pd.NaT, None]:
+            arr = np.array([n, np.datetime64('nat'), n])
+            self.assertEqual(pd.lib.infer_dtype(arr), 'datetime64')
+
+            arr = np.array([pd.NaT, n, np.datetime64('nat'), n])
+            self.assertEqual(pd.lib.infer_dtype(arr), 'datetime64')
+
+        arr = np.array([np.timedelta64('nat')], dtype=object)
+        self.assertEqual(pd.lib.infer_dtype(arr), 'timedelta')
+
+        for n in [np.nan, pd.NaT, None]:
+            arr = np.array([n, np.timedelta64('nat'), n])
+            self.assertEqual(pd.lib.infer_dtype(arr), 'timedelta')
+
+            arr = np.array([pd.NaT, n, np.timedelta64('nat'), n])
+            self.assertEqual(pd.lib.infer_dtype(arr), 'timedelta')
+
+        # datetime / timedelta mixed
+        arr = np.array([pd.NaT, np.datetime64('nat'),
+                        np.timedelta64('nat'), np.nan])
+        self.assertEqual(pd.lib.infer_dtype(arr), 'mixed')
+
+        arr = np.array([np.timedelta64('nat'), np.datetime64('nat')],
+                       dtype=object)
+        self.assertEqual(pd.lib.infer_dtype(arr), 'mixed')
+
+    def test_is_datetimelike_array_all_nan_nat_like(self):
+        arr = np.array([np.nan, pd.NaT, np.datetime64('nat')])
+        self.assertTrue(pd.lib.is_datetime_array(arr))
+        self.assertTrue(pd.lib.is_datetime64_array(arr))
+        self.assertFalse(pd.lib.is_timedelta_array(arr))
+        self.assertFalse(pd.lib.is_timedelta64_array(arr))
+        self.assertFalse(pd.lib.is_timedelta_or_timedelta64_array(arr))
+
+        arr = np.array([np.nan, pd.NaT, np.timedelta64('nat')])
+        self.assertFalse(pd.lib.is_datetime_array(arr))
+        self.assertFalse(pd.lib.is_datetime64_array(arr))
+        self.assertTrue(pd.lib.is_timedelta_array(arr))
+        self.assertTrue(pd.lib.is_timedelta64_array(arr))
+        self.assertTrue(pd.lib.is_timedelta_or_timedelta64_array(arr))
+
+        arr = np.array([np.nan, pd.NaT, np.datetime64('nat'),
+                        np.timedelta64('nat')])
+        self.assertFalse(pd.lib.is_datetime_array(arr))
+        self.assertFalse(pd.lib.is_datetime64_array(arr))
+        self.assertFalse(pd.lib.is_timedelta_array(arr))
+        self.assertFalse(pd.lib.is_timedelta64_array(arr))
+        self.assertFalse(pd.lib.is_timedelta_or_timedelta64_array(arr))
+
+        arr = np.array([np.nan, pd.NaT])
+        self.assertTrue(pd.lib.is_datetime_array(arr))
+        self.assertTrue(pd.lib.is_datetime64_array(arr))
+        self.assertTrue(pd.lib.is_timedelta_array(arr))
+        self.assertTrue(pd.lib.is_timedelta64_array(arr))
+        self.assertTrue(pd.lib.is_timedelta_or_timedelta64_array(arr))
+
+        arr = np.array([np.nan, np.nan], dtype=object)
+        self.assertFalse(pd.lib.is_datetime_array(arr))
+        self.assertFalse(pd.lib.is_datetime64_array(arr))
+        self.assertFalse(pd.lib.is_timedelta_array(arr))
+        self.assertFalse(pd.lib.is_timedelta64_array(arr))
+        self.assertFalse(pd.lib.is_timedelta_or_timedelta64_array(arr))
+
     def test_date(self):
 
         dates = [date(2012, 1, x) for x in range(1, 20)]
@@ -244,6 +445,13 @@ def test_categorical(self):
         result = lib.infer_dtype(Series(arr))
         self.assertEqual(result, 'categorical')
 
+    def test_is_period(self):
+        self.assertTrue(lib.is_period(pd.Period('2011-01', freq='M')))
+        self.assertFalse(lib.is_period(pd.PeriodIndex(['2011-01'], freq='M')))
+        self.assertFalse(lib.is_period(pd.Timestamp('2011-01')))
+        self.assertFalse(lib.is_period(1))
+        self.assertFalse(lib.is_period(np.nan))
+
 
 class TestConvert(tm.TestCase):
 
@@ -437,6 +645,7 @@ def test_convert_downcast_int64(self):
         result = lib.downcast_int64(arr, na_values)
         self.assert_numpy_array_equal(result, expected)
 
+
 if __name__ == '__main__':
     import nose