From ad190575aa75962d2d0eade2de81a5fe5a2e285b Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Wed, 12 Jan 2022 05:57:19 -0800 Subject: [PATCH] BUG: Timestamp(2020,1, 1, tzinfo=foo) GH#31929 (#45308) --- doc/source/whatsnew/v1.5.0.rst | 1 + pandas/_libs/tslibs/timestamps.pyx | 21 +++++++++++++++++++ .../scalar/timestamp/test_constructors.py | 20 ++++++++++++++++++ 3 files changed, 42 insertions(+) diff --git a/doc/source/whatsnew/v1.5.0.rst b/doc/source/whatsnew/v1.5.0.rst index 738ae8f5aba42..ecf31c93c5fa9 100644 --- a/doc/source/whatsnew/v1.5.0.rst +++ b/doc/source/whatsnew/v1.5.0.rst @@ -119,6 +119,7 @@ Categorical Datetimelike ^^^^^^^^^^^^ - Bug in :func:`to_datetime` with sequences of ``np.str_`` objects incorrectly raising (:issue:`32264`) +- Bug in :class:`Timestamp` construction when passing datetime components as positional arguments and ``tzinfo`` as a keyword argument incorrectly raising (:issue:`31929`) - Timedelta diff --git a/pandas/_libs/tslibs/timestamps.pyx b/pandas/_libs/tslibs/timestamps.pyx index 92a00d682a7e5..aad49bd70b120 100644 --- a/pandas/_libs/tslibs/timestamps.pyx +++ b/pandas/_libs/tslibs/timestamps.pyx @@ -1295,6 +1295,27 @@ class Timestamp(_Timestamp): # GH#17690 tzinfo must be a datetime.tzinfo object, ensured # by the cython annotation. if tz is not None: + if (is_integer_object(tz) + and is_integer_object(ts_input) + and is_integer_object(freq) + ): + # GH#31929 e.g. Timestamp(2019, 3, 4, 5, 6, tzinfo=foo) + # TODO(GH#45307): this will still be fragile to + # mixed-and-matched positional/keyword arguments + ts_input = datetime( + ts_input, + freq, + tz, + unit or 0, + year or 0, + month or 0, + day or 0, + fold=fold or 0, + ) + nanosecond = hour + tz = tzinfo + return cls(ts_input, nanosecond=nanosecond, tz=tz) + raise ValueError('Can provide at most one of tz, tzinfo') # User passed tzinfo instead of tz; avoid silently ignoring diff --git a/pandas/tests/scalar/timestamp/test_constructors.py b/pandas/tests/scalar/timestamp/test_constructors.py index b3deb1a57e5c3..be3560eb88bfe 100644 --- a/pandas/tests/scalar/timestamp/test_constructors.py +++ b/pandas/tests/scalar/timestamp/test_constructors.py @@ -2,6 +2,7 @@ from datetime import ( datetime, timedelta, + timezone, ) import dateutil.tz @@ -242,6 +243,25 @@ def test_constructor_tz_or_tzinfo(self): ] assert all(ts == stamps[0] for ts in stamps) + def test_constructor_positional_with_tzinfo(self): + # GH#31929 + ts = Timestamp(2020, 12, 31, tzinfo=timezone.utc) + expected = Timestamp("2020-12-31", tzinfo=timezone.utc) + assert ts == expected + + @pytest.mark.xfail(reason="GH#45307") + @pytest.mark.parametrize("kwd", ["nanosecond", "microsecond", "second", "minute"]) + def test_constructor_positional_keyword_mixed_with_tzinfo(self, kwd): + # TODO: if we passed microsecond with a keyword we would mess up + # xref GH#45307 + kwargs = {kwd: 4} + ts = Timestamp(2020, 12, 31, tzinfo=timezone.utc, **kwargs) + + td_kwargs = {kwd + "s": 4} + td = Timedelta(**td_kwargs) + expected = Timestamp("2020-12-31", tz=timezone.utc) + td + assert ts == expected + def test_constructor_positional(self): # see gh-10758 msg = (