From 197e9db309925038547c62d61dc87180bac1bb91 Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Tue, 27 Nov 2018 15:58:41 -0600 Subject: [PATCH 1/2] Ensure that DatetimeArray keeps reference to original data (cherry picked from commit 49f6495ba5ceef35a7a962f20e8b863c544592b7) --- pandas/_libs/tslibs/conversion.pyx | 4 ++-- pandas/tests/arrays/test_datetimelike.py | 9 +++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/pandas/_libs/tslibs/conversion.pyx b/pandas/_libs/tslibs/conversion.pyx index 9d6daf3d42523..f77ec61da4b0b 100644 --- a/pandas/_libs/tslibs/conversion.pyx +++ b/pandas/_libs/tslibs/conversion.pyx @@ -99,11 +99,11 @@ def ensure_datetime64ns(arr: ndarray, copy: bool=True): ivalues = arr.view(np.int64).ravel() - result = np.empty(shape, dtype='M8[ns]') + result = np.empty(shape, dtype=NS_DTYPE) iresult = result.ravel().view(np.int64) if len(iresult) == 0: - return result + return arr.astype(NS_DTYPE, copy=copy) unit = get_datetime64_unit(arr.flat[0]) if unit == NPY_FR_ns: diff --git a/pandas/tests/arrays/test_datetimelike.py b/pandas/tests/arrays/test_datetimelike.py index a1242e2481fed..902a3dda92bd6 100644 --- a/pandas/tests/arrays/test_datetimelike.py +++ b/pandas/tests/arrays/test_datetimelike.py @@ -167,6 +167,15 @@ def test_array_i8_dtype(self, tz_naive_fixture): assert result.base is expected.base assert result.base is not None + def test_from_array_keeps_base(self): + # Ensure that DatetimeArray._data.base isn't lost. + arr = np.array(['2000-01-01', '2000-01-02'], dtype='M8[ns]') + dta = DatetimeArray(arr) + + assert dta._data is arr + dta = DatetimeArray(arr[:0]) + assert dta._data.base is arr + def test_from_dti(self, tz_naive_fixture): tz = tz_naive_fixture dti = pd.date_range('2016-01-01', periods=3, tz=tz) From a77a24ab0cef5131df38c109cf600824100265cc Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Wed, 28 Nov 2018 07:37:05 -0600 Subject: [PATCH 2/2] REF: use .view Added tests for copy --- pandas/_libs/tslibs/conversion.pyx | 5 ++++- pandas/tests/tslibs/test_conversion.py | 12 ++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/pandas/_libs/tslibs/conversion.pyx b/pandas/_libs/tslibs/conversion.pyx index f77ec61da4b0b..bf5429c39e8fe 100644 --- a/pandas/_libs/tslibs/conversion.pyx +++ b/pandas/_libs/tslibs/conversion.pyx @@ -103,7 +103,10 @@ def ensure_datetime64ns(arr: ndarray, copy: bool=True): iresult = result.ravel().view(np.int64) if len(iresult) == 0: - return arr.astype(NS_DTYPE, copy=copy) + result = arr.view(NS_DTYPE) + if copy: + result = result.copy() + return result unit = get_datetime64_unit(arr.flat[0]) if unit == NPY_FR_ns: diff --git a/pandas/tests/tslibs/test_conversion.py b/pandas/tests/tslibs/test_conversion.py index fde1d1718a2a2..6bfc686ba830e 100644 --- a/pandas/tests/tslibs/test_conversion.py +++ b/pandas/tests/tslibs/test_conversion.py @@ -57,3 +57,15 @@ def test_tz_convert_corner(self, arr): timezones.maybe_get_tz('US/Eastern'), timezones.maybe_get_tz('Asia/Tokyo')) tm.assert_numpy_array_equal(result, arr) + + +class TestEnsureDatetime64NS(object): + @pytest.mark.parametrize('copy', [True, False]) + @pytest.mark.parametrize('dtype', ['M8[ns]', 'M8[s]']) + def test_length_zero_copy(self, dtype, copy): + arr = np.array([], dtype=dtype) + result = conversion.ensure_datetime64ns(arr, copy=copy) + if copy: + assert result.base is None + else: + assert result.base is arr