From 44ff21d64bc5f8641925dce4c59cc3c189d8b051 Mon Sep 17 00:00:00 2001 From: Matt Roeschke Date: Tue, 4 Apr 2017 22:59:22 -0700 Subject: [PATCH] BUG: Timestamp doesn't respect tz DST closes #11481 closes #15777 --- doc/source/whatsnew/v0.20.0.txt | 1 + pandas/_libs/tslib.pyx | 4 +++- pandas/tests/series/test_indexing.py | 6 +++--- pandas/tests/tseries/test_timezones.py | 24 ++++++++++++++++++++++-- 4 files changed, 29 insertions(+), 6 deletions(-) diff --git a/doc/source/whatsnew/v0.20.0.txt b/doc/source/whatsnew/v0.20.0.txt index 4e29e01415ba6..7664688ffa4f4 100644 --- a/doc/source/whatsnew/v0.20.0.txt +++ b/doc/source/whatsnew/v0.20.0.txt @@ -1124,6 +1124,7 @@ Conversion - Bug in ``Timestamp.replace`` now raises ``TypeError`` when incorrect argument names are given; previously this raised ``ValueError`` (:issue:`15240`) - Bug in ``Timestamp.replace`` with compat for passing long integers (:issue:`15030`) - Bug in ``Timestamp`` returning UTC based time/date attributes when a timezone was provided (:issue:`13303`) +- Bug in ``Timestamp`` incorrectly localizing timezones during construction (:issue:`11481`, :issue:`15777`) - Bug in ``TimedeltaIndex`` addition where overflow was being allowed without error (:issue:`14816`) - Bug in ``TimedeltaIndex`` raising a ``ValueError`` when boolean indexing with ``loc`` (:issue:`14946`) - Bug in catching an overflow in ``Timestamp`` + ``Timedelta/Offset`` operations (:issue:`15126`) diff --git a/pandas/_libs/tslib.pyx b/pandas/_libs/tslib.pyx index cc1439711c1d4..aa92a522ec978 100644 --- a/pandas/_libs/tslib.pyx +++ b/pandas/_libs/tslib.pyx @@ -1569,7 +1569,9 @@ cpdef convert_str_to_tsobject(object ts, object tz, object unit, ts = obj.value if tz is not None: # shift for _localize_tso - ts = tz_convert_single(ts, tz, 'UTC') + ts = tz_localize_to_utc(np.array([ts], dtype='i8'), tz, + ambiguous='raise', + errors='raise')[0] except ValueError: try: ts = parse_datetime_string( diff --git a/pandas/tests/series/test_indexing.py b/pandas/tests/series/test_indexing.py index 0b6c0c601ac72..48410c1c73479 100644 --- a/pandas/tests/series/test_indexing.py +++ b/pandas/tests/series/test_indexing.py @@ -1024,9 +1024,9 @@ def test_setitem_with_tz_dst(self): # scalar s = orig.copy() s[1] = pd.Timestamp('2011-01-01', tz=tz) - exp = pd.Series([pd.Timestamp('2016-11-06 00:00', tz=tz), - pd.Timestamp('2011-01-01 00:00', tz=tz), - pd.Timestamp('2016-11-06 02:00', tz=tz)]) + exp = pd.Series([pd.Timestamp('2016-11-06 00:00-04:00', tz=tz), + pd.Timestamp('2011-01-01 00:00-05:00', tz=tz), + pd.Timestamp('2016-11-06 01:00-05:00', tz=tz)]) tm.assert_series_equal(s, exp) s = orig.copy() diff --git a/pandas/tests/tseries/test_timezones.py b/pandas/tests/tseries/test_timezones.py index 1fc0e1b73df6b..e53fab2f6efc3 100644 --- a/pandas/tests/tseries/test_timezones.py +++ b/pandas/tests/tseries/test_timezones.py @@ -159,6 +159,26 @@ def test_timestamp_constructed_by_date_and_tz_explicit(self): self.assertEqual(result.hour, expected.hour) self.assertEqual(result, expected) + def test_timestamp_constructor_near_dst_boundary(self): + # GH 11481 & 15777 + # Naive string timestamps were being localized incorrectly + # with tz_convert_single instead of tz_localize_to_utc + + for tz in ['Europe/Brussels', 'Europe/Prague']: + result = Timestamp('2015-10-25 01:00', tz=tz) + expected = Timestamp('2015-10-25 01:00').tz_localize(tz) + self.assertEqual(result, expected) + + with tm.assertRaises(pytz.AmbiguousTimeError): + Timestamp('2015-10-25 02:00', tz=tz) + + result = Timestamp('2017-03-26 01:00', tz='Europe/Paris') + expected = Timestamp('2017-03-26 01:00').tz_localize('Europe/Paris') + self.assertEqual(result, expected) + + with tm.assertRaises(pytz.NonExistentTimeError): + Timestamp('2017-03-26 02:00', tz='Europe/Paris') + def test_timestamp_to_datetime_tzoffset(self): # tzoffset from dateutil.tz import tzoffset @@ -517,8 +537,8 @@ def f(): freq="H")) if dateutil.__version__ != LooseVersion('2.6.0'): # GH 14621 - self.assertEqual(times[-1], Timestamp('2013-10-27 01:00', tz=tz, - freq="H")) + self.assertEqual(times[-1], Timestamp('2013-10-27 01:00:00+0000', + tz=tz, freq="H")) def test_ambiguous_nat(self): tz = self.tz('US/Eastern')