Repository: pandas-dev/pandas

Issue:
BUG: explicit tz=None ignored in Timestamp constructor
```python
In [14]: Timestamp(datetime(2022, 1, 1, tzinfo=timezone.utc), tz=None)
Out[14]: Timestamp('2022-01-01 00:00:00+0000', tz='UTC')
```

If a user explicitly passes tz=None and a tzaware string or datetime object, we should raise.  This was fixed for DatetimeIndex in GH#48659



Discussion:

Patch:
diff --git a/doc/source/whatsnew/v3.0.0.rst b/doc/source/whatsnew/v3.0.0.rst
index e05cc87d1af14..66f6c10e36bdc 100644
--- a/doc/source/whatsnew/v3.0.0.rst
+++ b/doc/source/whatsnew/v3.0.0.rst
@@ -357,6 +357,7 @@ Categorical
 
 Datetimelike
 ^^^^^^^^^^^^
+- Bug in :class:`Timestamp` constructor failing to raise when ``tz=None`` is explicitly specified in conjunction with timezone-aware ``tzinfo`` or data (:issue:`48688`)
 - Bug in :func:`date_range` where the last valid timestamp would sometimes not be produced (:issue:`56134`)
 - Bug in :func:`date_range` where using a negative frequency value would not include all points between the start and end values (:issue:`56382`)
 -
diff --git a/pandas/_libs/tslibs/timestamps.pyx b/pandas/_libs/tslibs/timestamps.pyx
index d4cd90613ca5b..82daa6d942095 100644
--- a/pandas/_libs/tslibs/timestamps.pyx
+++ b/pandas/_libs/tslibs/timestamps.pyx
@@ -1751,7 +1751,7 @@ class Timestamp(_Timestamp):
         tzinfo_type tzinfo=None,
         *,
         nanosecond=None,
-        tz=None,
+        tz=_no_input,
         unit=None,
         fold=None,
     ):
@@ -1783,6 +1783,10 @@ class Timestamp(_Timestamp):
         _date_attributes = [year, month, day, hour, minute, second,
                             microsecond, nanosecond]
 
+        explicit_tz_none = tz is None
+        if tz is _no_input:
+            tz = None
+
         if tzinfo is not None:
             # GH#17690 tzinfo must be a datetime.tzinfo object, ensured
             #  by the cython annotation.
@@ -1883,6 +1887,11 @@ class Timestamp(_Timestamp):
         if ts.value == NPY_NAT:
             return NaT
 
+        if ts.tzinfo is not None and explicit_tz_none:
+            raise ValueError(
+                "Passed data is timezone-aware, incompatible with 'tz=None'."
+            )
+
         return create_timestamp_from_ts(ts.value, ts.dts, ts.tzinfo, ts.fold, ts.creso)
 
     def _round(self, freq, mode, ambiguous="raise", nonexistent="raise"):


Test patch:
diff --git a/pandas/tests/indexing/test_at.py b/pandas/tests/indexing/test_at.py
index d78694018749c..217ca74bd7fbd 100644
--- a/pandas/tests/indexing/test_at.py
+++ b/pandas/tests/indexing/test_at.py
@@ -136,7 +136,11 @@ def test_at_datetime_index(self, row):
 class TestAtSetItemWithExpansion:
     def test_at_setitem_expansion_series_dt64tz_value(self, tz_naive_fixture):
         # GH#25506
-        ts = Timestamp("2017-08-05 00:00:00+0100", tz=tz_naive_fixture)
+        ts = (
+            Timestamp("2017-08-05 00:00:00+0100", tz=tz_naive_fixture)
+            if tz_naive_fixture is not None
+            else Timestamp("2017-08-05 00:00:00+0100")
+        )
         result = Series(ts)
         result.at[1] = ts
         expected = Series([ts, ts])
diff --git a/pandas/tests/scalar/timestamp/test_constructors.py b/pandas/tests/scalar/timestamp/test_constructors.py
index bbda9d3ee7dce..4ebdea3733484 100644
--- a/pandas/tests/scalar/timestamp/test_constructors.py
+++ b/pandas/tests/scalar/timestamp/test_constructors.py
@@ -621,7 +621,6 @@ def test_constructor_with_stringoffset(self):
         ]
 
         timezones = [
-            (None, 0),
             ("UTC", 0),
             (pytz.utc, 0),
             ("Asia/Tokyo", 9),
@@ -1013,6 +1012,18 @@ def test_timestamp_constructed_by_date_and_tz(self, tz):
         assert result.hour == expected.hour
         assert result == expected
 
+    def test_explicit_tz_none(self):
+        # GH#48688
+        msg = "Passed data is timezone-aware, incompatible with 'tz=None'"
+        with pytest.raises(ValueError, match=msg):
+            Timestamp(datetime(2022, 1, 1, tzinfo=timezone.utc), tz=None)
+
+        with pytest.raises(ValueError, match=msg):
+            Timestamp("2022-01-01 00:00:00", tzinfo=timezone.utc, tz=None)
+
+        with pytest.raises(ValueError, match=msg):
+            Timestamp("2022-01-01 00:00:00-0400", tz=None)
+
 
 def test_constructor_ambiguous_dst():
     # GH 24329
diff --git a/pandas/tests/scalar/timestamp/test_formats.py b/pandas/tests/scalar/timestamp/test_formats.py
index b4493088acb31..e1299c272e5cc 100644
--- a/pandas/tests/scalar/timestamp/test_formats.py
+++ b/pandas/tests/scalar/timestamp/test_formats.py
@@ -118,7 +118,7 @@ def test_repr(self, date, freq, tz):
     def test_repr_utcoffset(self):
         # This can cause the tz field to be populated, but it's redundant to
         # include this information in the date-string.
-        date_with_utc_offset = Timestamp("2014-03-13 00:00:00-0400", tz=None)
+        date_with_utc_offset = Timestamp("2014-03-13 00:00:00-0400")
         assert "2014-03-13 00:00:00-0400" in repr(date_with_utc_offset)
         assert "tzoffset" not in repr(date_with_utc_offset)
         assert "UTC-04:00" in repr(date_with_utc_offset)