diff --git a/doc/source/whatsnew/v0.23.0.txt b/doc/source/whatsnew/v0.23.0.txt index 3f300deddebeb..1273433730162 100644 --- a/doc/source/whatsnew/v0.23.0.txt +++ b/doc/source/whatsnew/v0.23.0.txt @@ -350,7 +350,7 @@ Reshaping Numeric ^^^^^^^ -- +- Bug in :func:`Series.__sub__` subtracting a non-nanosecond ``np.datetime64`` object from a ``Series`` gave incorrect results (:issue:`7996`) - - diff --git a/pandas/core/ops.py b/pandas/core/ops.py index ac9ca03c13973..05ec7f41b0c66 100644 --- a/pandas/core/ops.py +++ b/pandas/core/ops.py @@ -28,7 +28,7 @@ is_datetimelike_v_numeric, is_integer_dtype, is_categorical_dtype, is_object_dtype, is_timedelta64_dtype, - is_datetime64_dtype, is_datetime64tz_dtype, + is_datetime64_dtype, is_datetime64tz_dtype, is_datetime64_ns_dtype, is_bool_dtype, is_datetimetz, is_list_like, is_offsetlike, is_scalar, @@ -527,6 +527,11 @@ def _convert_to_array(self, values, name=None, other=None): elif not (isinstance(values, (np.ndarray, ABCSeries)) and is_datetime64_dtype(values)): values = libts.array_to_datetime(values) + elif (is_datetime64_dtype(values) and + not is_datetime64_ns_dtype(values)): + # GH#7996 e.g. np.datetime64('2013-01-01') is datetime64[D] + values = values.astype('datetime64[ns]') + elif inferred_type in ('timedelta', 'timedelta64'): # have a timedelta, convert to to ns here values = to_timedelta(values, errors='coerce', box=False) diff --git a/pandas/tests/series/test_operators.py b/pandas/tests/series/test_operators.py index 4adbdbca82fd2..433e3cf440cbd 100644 --- a/pandas/tests/series/test_operators.py +++ b/pandas/tests/series/test_operators.py @@ -1004,6 +1004,33 @@ def test_operators_timedelta64_with_timedelta_invalid(self, scalar_td): class TestDatetimeSeriesArithmetic(object): + @pytest.mark.parametrize( + 'box, assert_func', + [(Series, tm.assert_series_equal), + (pd.Index, tm.assert_index_equal)]) + def test_sub_datetime64_not_ns(self, box, assert_func): + # GH#7996 + dt64 = np.datetime64('2013-01-01') + assert dt64.dtype == 'datetime64[D]' + + obj = box(date_range('20130101', periods=3)) + res = obj - dt64 + expected = box([Timedelta(days=0), Timedelta(days=1), + Timedelta(days=2)]) + assert_func(res, expected) + + res = dt64 - obj + assert_func(res, -expected) + + @pytest.mark.xfail(reason='GH#7996 datetime64 units not converted to nano') + def test_frame_sub_datetime64_not_ns(self): + df = pd.DataFrame(date_range('20130101', periods=3)) + dt64 = np.datetime64('2013-01-01') + assert dt64.dtype == 'datetime64[D]' + res = df - dt64 + expected = pd.DataFrame([Timedelta(days=0), Timedelta(days=1), + Timedelta(days=2)]) + tm.assert_frame_equal(res, expected) def test_operators_datetimelike(self): def run_ops(ops, get_ser, test_ser):