Skip to content

Commit a2ba5b3

Browse files
Stefan Scherfkejreback
Stefan Scherfke
authored andcommitted
BUG: in Timestamp.replace when replacing tzinfo around DST changes
closes pandas-dev#15683
1 parent cbb090f commit a2ba5b3

File tree

3 files changed

+32
-10
lines changed

3 files changed

+32
-10
lines changed

doc/source/whatsnew/v0.21.0.txt

+1
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,7 @@ Conversion
487487
- Bug in ``IntervalIndex.is_non_overlapping_monotonic`` when intervals are closed on both sides and overlap at a point (:issue:`16560`)
488488
- Bug in :func:`Series.fillna` returns frame when ``inplace=True`` and ``value`` is dict (:issue:`16156`)
489489
- Bug in :attr:`Timestamp.weekday_name` returning a UTC-based weekday name when localized to a timezone (:issue:`17354`)
490+
- Bug in ``Timestamp.replace`` when replacing ``tzinfo`` around DST changes (:issue:`15683`)
490491

491492
Indexing
492493
^^^^^^^^

pandas/_libs/tslib.pyx

+10-10
Original file line numberDiff line numberDiff line change
@@ -683,14 +683,16 @@ class Timestamp(_Timestamp):
683683

684684
cdef:
685685
pandas_datetimestruct dts
686-
int64_t value
687-
object _tzinfo, result, k, v
686+
int64_t value, value_tz, offset
687+
object _tzinfo, result, k, v, ts_input
688688

689689
# set to naive if needed
690690
_tzinfo = self.tzinfo
691691
value = self.value
692692
if _tzinfo is not None:
693-
value = tz_convert_single(value, 'UTC', _tzinfo)
693+
value_tz = tz_convert_single(value, _tzinfo, 'UTC')
694+
offset = value - value_tz
695+
value += offset
694696

695697
# setup components
696698
pandas_datetime_to_datetimestruct(value, PANDAS_FR_ns, &dts)
@@ -724,16 +726,14 @@ class Timestamp(_Timestamp):
724726
_tzinfo = tzinfo
725727

726728
# reconstruct & check bounds
727-
value = pandas_datetimestruct_to_datetime(PANDAS_FR_ns, &dts)
729+
ts_input = datetime(dts.year, dts.month, dts.day, dts.hour, dts.min,
730+
dts.sec, dts.us, tzinfo=_tzinfo)
731+
ts = convert_to_tsobject(ts_input, _tzinfo, None, 0, 0)
732+
value = ts.value + (dts.ps // 1000)
728733
if value != NPY_NAT:
729734
_check_dts_bounds(&dts)
730735

731-
# set tz if needed
732-
if _tzinfo is not None:
733-
value = tz_convert_single(value, _tzinfo, 'UTC')
734-
735-
result = create_timestamp_from_ts(value, dts, _tzinfo, self.freq)
736-
return result
736+
return create_timestamp_from_ts(value, dts, _tzinfo, self.freq)
737737

738738
def isoformat(self, sep='T'):
739739
base = super(_Timestamp, self).isoformat(sep=sep)

pandas/tests/tseries/test_timezones.py

+21
Original file line numberDiff line numberDiff line change
@@ -1269,6 +1269,27 @@ def test_ambiguous_compat(self):
12691269
assert (result_pytz.to_pydatetime().tzname() ==
12701270
result_dateutil.to_pydatetime().tzname())
12711271

1272+
def test_replace_tzinfo(self):
1273+
# GH 15683
1274+
dt = datetime(2016, 3, 27, 1)
1275+
tzinfo = pytz.timezone('CET').localize(dt, is_dst=False).tzinfo
1276+
1277+
result_dt = dt.replace(tzinfo=tzinfo)
1278+
result_pd = Timestamp(dt).replace(tzinfo=tzinfo)
1279+
1280+
if hasattr(result_dt, 'timestamp'): # New method in Py 3.3
1281+
assert result_dt.timestamp() == result_pd.timestamp()
1282+
assert result_dt == result_pd
1283+
assert result_dt == result_pd.to_pydatetime()
1284+
1285+
result_dt = dt.replace(tzinfo=tzinfo).replace(tzinfo=None)
1286+
result_pd = Timestamp(dt).replace(tzinfo=tzinfo).replace(tzinfo=None)
1287+
1288+
if hasattr(result_dt, 'timestamp'): # New method in Py 3.3
1289+
assert result_dt.timestamp() == result_pd.timestamp()
1290+
assert result_dt == result_pd
1291+
assert result_dt == result_pd.to_pydatetime()
1292+
12721293
def test_index_equals_with_tz(self):
12731294
left = date_range('1/1/2011', periods=100, freq='H', tz='utc')
12741295
right = date_range('1/1/2011', periods=100, freq='H', tz='US/Eastern')

0 commit comments

Comments
 (0)