From af982485be3ef66e945f0b0b1fdf7b90c28912e7 Mon Sep 17 00:00:00 2001 From: Brock Date: Wed, 26 Oct 2022 16:07:43 -0700 Subject: [PATCH 1/3] DEPR: to_datetime('now') match Timestamp('now') --- doc/source/whatsnew/v2.0.0.rst | 1 + pandas/_libs/tslib.pyx | 18 ++++-------------- pandas/tests/tools/test_to_datetime.py | 24 +++++++----------------- 3 files changed, 12 insertions(+), 31 deletions(-) diff --git a/doc/source/whatsnew/v2.0.0.rst b/doc/source/whatsnew/v2.0.0.rst index 78ea78ec97a3a..8ebd611468f7d 100644 --- a/doc/source/whatsnew/v2.0.0.rst +++ b/doc/source/whatsnew/v2.0.0.rst @@ -252,6 +252,7 @@ Removal of prior version deprecations/changes - Removed the ``display.column_space`` option in favor of ``df.to_string(col_space=...)`` (:issue:`47280`) - Removed the deprecated method ``mad`` from pandas classes (:issue:`11787`) - Removed the deprecated method ``tshift`` from pandas classes (:issue:`11631`) +- Changed the behavior of :func:`to_datetime` with argument "now" with ``utc=False`` to match ``Timestamp("now")`` (:issue:`18705`) - Changed behavior of :class:`DataFrame` constructor given floating-point ``data`` and an integer ``dtype``, when the data cannot be cast losslessly, the floating point dtype is retained, matching :class:`Series` behavior (:issue:`41170`) - Changed behavior of :class:`DataFrame` constructor when passed a ``dtype`` (other than int) that the data cannot be cast to; it now raises instead of silently ignoring the dtype (:issue:`41733`) - Changed the behavior of :class:`Series` constructor, it will no longer infer a datetime64 or timedelta64 dtype from string entries (:issue:`41731`) diff --git a/pandas/_libs/tslib.pyx b/pandas/_libs/tslib.pyx index 03331f54db892..d15ca1013c798 100644 --- a/pandas/_libs/tslib.pyx +++ b/pandas/_libs/tslib.pyx @@ -1,5 +1,3 @@ -import warnings - cimport cython from cpython.datetime cimport ( PyDate_Check, @@ -9,8 +7,6 @@ from cpython.datetime cimport ( tzinfo, ) -from pandas.util._exceptions import find_stack_level - # import datetime C API import_datetime() @@ -855,17 +851,11 @@ cdef inline bint _parse_today_now(str val, int64_t* iresult, bint utc): # We delay this check for as long as possible # because it catches relatively rare cases if val == "now": - iresult[0] = Timestamp.utcnow().value - if not utc: + if utc: + iresult[0] = Timestamp.utcnow().value + else: # GH#18705 make sure to_datetime("now") matches Timestamp("now") - warnings.warn( - "The parsing of 'now' in pd.to_datetime without `utc=True` is " - "deprecated. In a future version, this will match Timestamp('now') " - "and Timestamp.now()", - FutureWarning, - stacklevel=find_stack_level(), - ) - + iresult[0] = Timestamp("now").value return True elif val == "today": iresult[0] = Timestamp.today().value diff --git a/pandas/tests/tools/test_to_datetime.py b/pandas/tests/tools/test_to_datetime.py index f524bc18793d8..c3b4159c2cbfc 100644 --- a/pandas/tests/tools/test_to_datetime.py +++ b/pandas/tests/tools/test_to_datetime.py @@ -626,20 +626,15 @@ def test_to_datetime_unparsable_ignore(self): def test_to_datetime_now(self): # See GH#18666 with tm.set_timezone("US/Eastern"): - msg = "The parsing of 'now' in pd.to_datetime" - with tm.assert_produces_warning( - FutureWarning, match=msg, check_stacklevel=False - ): - # checking stacklevel is tricky because we go through cython code - # GH#18705 - npnow = np.datetime64("now").astype("datetime64[ns]") - pdnow = to_datetime("now") - pdnow2 = to_datetime(["now"])[0] + # GH#18705 + now = Timestamp("now") + pdnow = to_datetime("now") + pdnow2 = to_datetime(["now"])[0] # These should all be equal with infinite perf; this gives # a generous margin of 10 seconds - assert abs(pdnow.value - npnow.astype(np.int64)) < 1e10 - assert abs(pdnow2.value - npnow.astype(np.int64)) < 1e10 + assert abs(pdnow.value - now.value) < 1e10 + assert abs(pdnow2.value - now.value) < 1e10 assert pdnow.tzinfo is None assert pdnow2.tzinfo is None @@ -673,12 +668,7 @@ def test_to_datetime_today(self, tz): @pytest.mark.parametrize("arg", ["now", "today"]) def test_to_datetime_today_now_unicode_bytes(self, arg): - warn = FutureWarning if arg == "now" else None - msg = "The parsing of 'now' in pd.to_datetime" - with tm.assert_produces_warning(warn, match=msg, check_stacklevel=False): - # checking stacklevel is tricky because we go through cython code - # GH#18705 - to_datetime([arg]) + to_datetime([arg]) @pytest.mark.parametrize( "dt", [np.datetime64("2000-01-01"), np.datetime64("2000-01-02")] From dd7b6faab4a041017df8bf06d85b3c8eedca6bf7 Mon Sep 17 00:00:00 2001 From: Brock Date: Thu, 27 Oct 2022 13:23:53 -0700 Subject: [PATCH 2/3] perf --- pandas/_libs/tslib.pyx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pandas/_libs/tslib.pyx b/pandas/_libs/tslib.pyx index d15ca1013c798..f6abfc5657709 100644 --- a/pandas/_libs/tslib.pyx +++ b/pandas/_libs/tslib.pyx @@ -855,7 +855,9 @@ cdef inline bint _parse_today_now(str val, int64_t* iresult, bint utc): iresult[0] = Timestamp.utcnow().value else: # GH#18705 make sure to_datetime("now") matches Timestamp("now") - iresult[0] = Timestamp("now").value + # Note using np.datetime64 here is equivalent to Timestamp("now") + # and Timestamp.now(), but about 2x faster + iresult[0] = np.datetime64("now", "ns").view("i8") return True elif val == "today": iresult[0] = Timestamp.today().value From f1a256bf854bb8e7241d8f53a854d0bf5e18bd86 Mon Sep 17 00:00:00 2001 From: Brock Date: Thu, 27 Oct 2022 15:01:40 -0700 Subject: [PATCH 3/3] try again --- pandas/_libs/tslib.pyx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pandas/_libs/tslib.pyx b/pandas/_libs/tslib.pyx index f6abfc5657709..d7c0c91332e02 100644 --- a/pandas/_libs/tslib.pyx +++ b/pandas/_libs/tslib.pyx @@ -855,9 +855,8 @@ cdef inline bint _parse_today_now(str val, int64_t* iresult, bint utc): iresult[0] = Timestamp.utcnow().value else: # GH#18705 make sure to_datetime("now") matches Timestamp("now") - # Note using np.datetime64 here is equivalent to Timestamp("now") - # and Timestamp.now(), but about 2x faster - iresult[0] = np.datetime64("now", "ns").view("i8") + # Note using Timestamp.now() is faster than Timestamp("now") + iresult[0] = Timestamp.now().value return True elif val == "today": iresult[0] = Timestamp.today().value