From d6fdbee9f31f621f5177f65abc4525c822ab2e97 Mon Sep 17 00:00:00 2001 From: yuanx749 Date: Fri, 1 Dec 2023 22:50:29 +0800 Subject: [PATCH 1/5] BUG: Fix `Timestamp('now')` and `Timestamp.now` unit inconsistency --- doc/source/whatsnew/v2.1.4.rst | 1 + pandas/_libs/tslibs/conversion.pyx | 2 +- pandas/tests/scalar/timestamp/test_constructors.py | 14 ++++++++++++++ pandas/tests/tools/test_to_datetime.py | 4 ++-- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/doc/source/whatsnew/v2.1.4.rst b/doc/source/whatsnew/v2.1.4.rst index 9e3eb90436642..34dbd58e47c58 100644 --- a/doc/source/whatsnew/v2.1.4.rst +++ b/doc/source/whatsnew/v2.1.4.rst @@ -22,6 +22,7 @@ Fixed regressions Bug fixes ~~~~~~~~~ - Bug in :class:`Series` constructor raising DeprecationWarning when ``index`` is a list of :class:`Series` (:issue:`55228`) +- Bug in :class:`Timestamp` construction with ``ts_input=now`` or ``ts_input=today`` giving a different unit from :meth:`Timestamp.now` or :meth:`Timestamp.today` (:issue:`55879`) - Bug in :meth:`Index.__getitem__` returning wrong result for Arrow dtypes and negative stepsize (:issue:`55832`) - Fixed bug in :func:`to_numeric` converting to extension dtype for ``string[pyarrow_numpy]`` dtype (:issue:`56179`) - Fixed bug in :meth:`DataFrame.__setitem__` casting :class:`Index` with object-dtype to PyArrow backed strings when ``infer_string`` option is set (:issue:`55638`) diff --git a/pandas/_libs/tslibs/conversion.pyx b/pandas/_libs/tslibs/conversion.pyx index 5ad9a648c52a2..244606ade4d0b 100644 --- a/pandas/_libs/tslibs/conversion.pyx +++ b/pandas/_libs/tslibs/conversion.pyx @@ -646,7 +646,7 @@ cdef _TSObject convert_str_to_tsobject(str ts, tzinfo tz, reso = get_supported_reso(out_bestunit) return convert_datetime_to_tsobject(dt, tz, nanos=nanos, reso=reso) - return convert_datetime_to_tsobject(dt, tz) + return convert_datetime_to_tsobject(dt, tz, nanos=0, reso=NPY_FR_us) cdef check_overflows(_TSObject obj, NPY_DATETIMEUNIT reso=NPY_FR_ns): diff --git a/pandas/tests/scalar/timestamp/test_constructors.py b/pandas/tests/scalar/timestamp/test_constructors.py index 91314a497b1fb..9b0bf88635276 100644 --- a/pandas/tests/scalar/timestamp/test_constructors.py +++ b/pandas/tests/scalar/timestamp/test_constructors.py @@ -464,6 +464,20 @@ def test_constructor_str_infer_reso(self): ts = Timestamp("2020-01-01 00+00:00") assert ts.unit == "s" + def test_now_today_unit(self): + # GH#55879 + ts_now_from_method = Timestamp.now() + ts_now_from_string = Timestamp("now") + ts_today_from_method = Timestamp.today() + ts_today_from_string = Timestamp("today") + assert ( + ts_now_from_method.unit + == ts_now_from_string.unit + == ts_today_from_method.unit + == ts_today_from_string.unit + == "us" + ) + class TestTimestampConstructors: def test_weekday_but_no_day_raises(self): diff --git a/pandas/tests/tools/test_to_datetime.py b/pandas/tests/tools/test_to_datetime.py index f74fe459eb4d6..de5d67e6bd25f 100644 --- a/pandas/tests/tools/test_to_datetime.py +++ b/pandas/tests/tools/test_to_datetime.py @@ -1040,7 +1040,7 @@ def test_to_datetime_now(self): # See GH#18666 with tm.set_timezone("US/Eastern"): # GH#18705 - now = Timestamp("now") + now = Timestamp("now").as_unit("ns") pdnow = to_datetime("now") pdnow2 = to_datetime(["now"])[0] @@ -1066,7 +1066,7 @@ def test_to_datetime_today(self, tz): pdtoday = to_datetime("today") pdtoday2 = to_datetime(["today"])[0] - tstoday = Timestamp("today") + tstoday = Timestamp("today").as_unit("ns") tstoday2 = Timestamp.today().as_unit("ns") # These should all be equal with infinite perf; this gives From 1af8ab0911a3a1958555a71665c7299af71285c4 Mon Sep 17 00:00:00 2001 From: yuanx749 Date: Sat, 2 Dec 2023 13:51:47 +0800 Subject: [PATCH 2/5] Move return --- pandas/_libs/tslibs/conversion.pyx | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/pandas/_libs/tslibs/conversion.pyx b/pandas/_libs/tslibs/conversion.pyx index 244606ade4d0b..c4f37f86a84ee 100644 --- a/pandas/_libs/tslibs/conversion.pyx +++ b/pandas/_libs/tslibs/conversion.pyx @@ -594,15 +594,12 @@ cdef _TSObject convert_str_to_tsobject(str ts, tzinfo tz, obj.value = NPY_NAT obj.tzinfo = tz return obj - elif ts == "now": + elif ts in ("now", "today"): # Issue 9000, we short-circuit rather than going # into np_datetime_strings which returns utc dt = datetime.now(tz) - elif ts == "today": - # Issue 9000, we short-circuit rather than going - # into np_datetime_strings which returns a normalized datetime - dt = datetime.now(tz) # equiv: datetime.today().replace(tzinfo=tz) + return convert_datetime_to_tsobject(dt, tz, nanos=0, reso=NPY_FR_us) else: string_to_dts_failed = string_to_dts( ts, &dts, &out_bestunit, &out_local, @@ -646,7 +643,7 @@ cdef _TSObject convert_str_to_tsobject(str ts, tzinfo tz, reso = get_supported_reso(out_bestunit) return convert_datetime_to_tsobject(dt, tz, nanos=nanos, reso=reso) - return convert_datetime_to_tsobject(dt, tz, nanos=0, reso=NPY_FR_us) + return convert_datetime_to_tsobject(dt, tz) cdef check_overflows(_TSObject obj, NPY_DATETIMEUNIT reso=NPY_FR_ns): From c24887eba4be688c2e4956cd66e2996907ff1130 Mon Sep 17 00:00:00 2001 From: Xiao Yuan Date: Tue, 5 Dec 2023 10:25:01 +0800 Subject: [PATCH 3/5] Update doc/source/whatsnew/v2.1.4.rst Co-authored-by: Matthew Roeschke <10647082+mroeschke@users.noreply.github.com> --- doc/source/whatsnew/v2.1.4.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v2.1.4.rst b/doc/source/whatsnew/v2.1.4.rst index 34dbd58e47c58..c65d34fcff496 100644 --- a/doc/source/whatsnew/v2.1.4.rst +++ b/doc/source/whatsnew/v2.1.4.rst @@ -22,7 +22,7 @@ Fixed regressions Bug fixes ~~~~~~~~~ - Bug in :class:`Series` constructor raising DeprecationWarning when ``index`` is a list of :class:`Series` (:issue:`55228`) -- Bug in :class:`Timestamp` construction with ``ts_input=now`` or ``ts_input=today`` giving a different unit from :meth:`Timestamp.now` or :meth:`Timestamp.today` (:issue:`55879`) +- Bug in :class:`Timestamp` construction with ``ts_input="now"`` or ``ts_input="today"`` giving a different unit from :meth:`Timestamp.now` or :meth:`Timestamp.today` (:issue:`55879`) - Bug in :meth:`Index.__getitem__` returning wrong result for Arrow dtypes and negative stepsize (:issue:`55832`) - Fixed bug in :func:`to_numeric` converting to extension dtype for ``string[pyarrow_numpy]`` dtype (:issue:`56179`) - Fixed bug in :meth:`DataFrame.__setitem__` casting :class:`Index` with object-dtype to PyArrow backed strings when ``infer_string`` option is set (:issue:`55638`) From f34f18f83cb864b2c35a95d8b5ca89ccf6164a9e Mon Sep 17 00:00:00 2001 From: yuanx749 Date: Tue, 5 Dec 2023 10:49:41 +0800 Subject: [PATCH 4/5] Resolve --- pandas/_libs/tslibs/conversion.pyx | 2 -- .../tests/scalar/timestamp/test_constructors.py | 17 +++++------------ 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/pandas/_libs/tslibs/conversion.pyx b/pandas/_libs/tslibs/conversion.pyx index c4f37f86a84ee..1bc547d1fd3bf 100644 --- a/pandas/_libs/tslibs/conversion.pyx +++ b/pandas/_libs/tslibs/conversion.pyx @@ -643,8 +643,6 @@ cdef _TSObject convert_str_to_tsobject(str ts, tzinfo tz, reso = get_supported_reso(out_bestunit) return convert_datetime_to_tsobject(dt, tz, nanos=nanos, reso=reso) - return convert_datetime_to_tsobject(dt, tz) - cdef check_overflows(_TSObject obj, NPY_DATETIMEUNIT reso=NPY_FR_ns): """ diff --git a/pandas/tests/scalar/timestamp/test_constructors.py b/pandas/tests/scalar/timestamp/test_constructors.py index 9b0bf88635276..98e4d581dc104 100644 --- a/pandas/tests/scalar/timestamp/test_constructors.py +++ b/pandas/tests/scalar/timestamp/test_constructors.py @@ -464,19 +464,12 @@ def test_constructor_str_infer_reso(self): ts = Timestamp("2020-01-01 00+00:00") assert ts.unit == "s" - def test_now_today_unit(self): + @pytest.mark.parametrize("method", ["now", "today"]) + def test_now_today_unit(self, method): # GH#55879 - ts_now_from_method = Timestamp.now() - ts_now_from_string = Timestamp("now") - ts_today_from_method = Timestamp.today() - ts_today_from_string = Timestamp("today") - assert ( - ts_now_from_method.unit - == ts_now_from_string.unit - == ts_today_from_method.unit - == ts_today_from_string.unit - == "us" - ) + ts_from_method = getattr(Timestamp, method)() + ts_from_string = Timestamp(method) + assert ts_from_method.unit == ts_from_string.unit == "us" class TestTimestampConstructors: From 25778ebce7758cc532e7651eda4179ff9d118e20 Mon Sep 17 00:00:00 2001 From: yuanx749 Date: Thu, 7 Dec 2023 10:21:06 +0800 Subject: [PATCH 5/5] not use tuple in cython for perf --- pandas/_libs/tslibs/conversion.pyx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pandas/_libs/tslibs/conversion.pyx b/pandas/_libs/tslibs/conversion.pyx index 1bc547d1fd3bf..12c3fb5fc5133 100644 --- a/pandas/_libs/tslibs/conversion.pyx +++ b/pandas/_libs/tslibs/conversion.pyx @@ -594,10 +594,15 @@ cdef _TSObject convert_str_to_tsobject(str ts, tzinfo tz, obj.value = NPY_NAT obj.tzinfo = tz return obj - elif ts in ("now", "today"): + elif ts == "now": # Issue 9000, we short-circuit rather than going # into np_datetime_strings which returns utc dt = datetime.now(tz) + return convert_datetime_to_tsobject(dt, tz, nanos=0, reso=NPY_FR_us) + elif ts == "today": + # Issue 9000, we short-circuit rather than going + # into np_datetime_strings which returns a normalized datetime + dt = datetime.now(tz) # equiv: datetime.today().replace(tzinfo=tz) return convert_datetime_to_tsobject(dt, tz, nanos=0, reso=NPY_FR_us) else: