diff --git a/doc/source/whatsnew/v3.0.0.rst b/doc/source/whatsnew/v3.0.0.rst index 0e779ff470fcc..441e12a4156b7 100644 --- a/doc/source/whatsnew/v3.0.0.rst +++ b/doc/source/whatsnew/v3.0.0.rst @@ -446,6 +446,7 @@ Other Deprecations - Deprecated allowing non-keyword arguments in :meth:`Series.to_string` except ``buf``. (:issue:`57280`) - Deprecated behavior of :meth:`.DataFrameGroupBy.groups` and :meth:`.SeriesGroupBy.groups`, in a future version ``groups`` by one element list will return tuple instead of scalar. (:issue:`58858`) - Deprecated behavior of :meth:`Series.dt.to_pytimedelta`, in a future version this will return a :class:`Series` containing python ``datetime.timedelta`` objects instead of an ``ndarray`` of timedelta; this matches the behavior of other :meth:`Series.dt` properties. (:issue:`57463`) +- Deprecated empty string in :class:`Timestamp` (:issue:`61149`) - Deprecated lowercase strings ``d``, ``b`` and ``c`` denoting frequencies in :class:`Day`, :class:`BusinessDay` and :class:`CustomBusinessDay` in favour of ``D``, ``B`` and ``C`` (:issue:`58998`) - Deprecated lowercase strings ``w``, ``w-mon``, ``w-tue``, etc. denoting frequencies in :class:`Week` in favour of ``W``, ``W-MON``, ``W-TUE``, etc. (:issue:`58998`) - Deprecated parameter ``method`` in :meth:`DataFrame.reindex_like` / :meth:`Series.reindex_like` (:issue:`58667`) diff --git a/pandas/_libs/tslibs/timestamps.pyx b/pandas/_libs/tslibs/timestamps.pyx index 390267db8267f..7777e2073d738 100644 --- a/pandas/_libs/tslibs/timestamps.pyx +++ b/pandas/_libs/tslibs/timestamps.pyx @@ -2657,6 +2657,13 @@ class Timestamp(_Timestamp): tzinfo is None): return ts_input elif isinstance(ts_input, str): + if ts_input == "": + warnings.warn( + "Passing an empty string to Timestamp is deprecated and will raise " + "a ValueError in a future version. Use `pd.NaT` directly instead.", + FutureWarning, + stacklevel = find_stack_level() + ) # User passed a date string to parse. # Check that the user didn't also pass a date attribute kwarg. if any(arg is not None for arg in _date_attributes): diff --git a/pandas/core/arrays/datetimes.py b/pandas/core/arrays/datetimes.py index b31c543188282..0490b98cfeebe 100644 --- a/pandas/core/arrays/datetimes.py +++ b/pandas/core/arrays/datetimes.py @@ -547,6 +547,8 @@ def _unbox_scalar(self, value) -> np.datetime64: return value.as_unit(self.unit, round_ok=False).asm8 def _scalar_from_string(self, value) -> Timestamp | NaTType: + if value == "": + value = NaT return Timestamp(value, tz=self.tz) def _check_compatible_with(self, other) -> None: diff --git a/pandas/tests/scalar/test_nat.py b/pandas/tests/scalar/test_nat.py index b20df43dd49a6..c274670e2acdc 100644 --- a/pandas/tests/scalar/test_nat.py +++ b/pandas/tests/scalar/test_nat.py @@ -109,7 +109,13 @@ def test_nat_vector_field_access(): "value", [None, np.nan, iNaT, float("nan"), NaT, "NaT", "nat", "", "NAT"] ) def test_identity(klass, value): - assert klass(value) is NaT + if value == "" and klass == Timestamp: + msg = "Passing an empty string to Timestamp" + with tm.assert_produces_warning(FutureWarning, match=msg): + result = klass(value) + else: + result = klass(value) + assert result is NaT @pytest.mark.parametrize("klass", [Timestamp, Timedelta]) diff --git a/pandas/tests/scalar/timestamp/test_constructors.py b/pandas/tests/scalar/timestamp/test_constructors.py index 2c97c4a32e0aa..d7721b008ccad 100644 --- a/pandas/tests/scalar/timestamp/test_constructors.py +++ b/pandas/tests/scalar/timestamp/test_constructors.py @@ -62,6 +62,11 @@ def test_constructor_float_not_round_with_YM_unit_raises(self): with pytest.raises(ValueError, match=msg): Timestamp(150.5, unit="M") + def test_constructor_with_empty_string(self): + msg = "Passing an empty string to Timestamp" + with tm.assert_produces_warning(FutureWarning, match=msg): + Timestamp("") + @pytest.mark.parametrize( "value, check_kwargs", [