Skip to content

Commit 96d7a95

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

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
@@ -430,6 +430,7 @@ Conversion
430430
- Bug in ``IntervalIndex.is_non_overlapping_monotonic`` when intervals are closed on both sides and overlap at a point (:issue:`16560`)
431431
- Bug in :func:`Series.fillna` returns frame when ``inplace=True`` and ``value`` is dict (:issue:`16156`)
432432
- Bug in :attr:`Timestamp.weekday_name` returning a UTC-based weekday name when localized to a timezone (:issue:`17354`)
433+
- Bug in ``Timestamp.replace`` when replacing ``tzinfo`` around DST changes (:issue:`15683`)
433434

434435
Indexing
435436
^^^^^^^^

pandas/_libs/tslib.pyx

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

700700
cdef:
701701
pandas_datetimestruct dts
702-
int64_t value
703-
object _tzinfo, result, k, v
702+
int64_t value, value_tz, offset
703+
object _tzinfo, result, k, v, ts_input
704704

705705
# set to naive if needed
706706
_tzinfo = self.tzinfo
707707
value = self.value
708708
if _tzinfo is not None:
709-
value = tz_convert_single(value, 'UTC', _tzinfo)
709+
value_tz = tz_convert_single(value, _tzinfo, 'UTC')
710+
offset = value - value_tz
711+
value += offset
710712

711713
# setup components
712714
pandas_datetime_to_datetimestruct(value, PANDAS_FR_ns, &dts)
@@ -740,16 +742,14 @@ class Timestamp(_Timestamp):
740742
_tzinfo = tzinfo
741743

742744
# reconstruct & check bounds
743-
value = pandas_datetimestruct_to_datetime(PANDAS_FR_ns, &dts)
745+
ts_input = datetime(dts.year, dts.month, dts.day, dts.hour, dts.min,
746+
dts.sec, dts.us, tzinfo=_tzinfo)
747+
ts = convert_to_tsobject(ts_input, _tzinfo, None, 0, 0)
748+
value = ts.value + (dts.ps // 1000)
744749
if value != NPY_NAT:
745750
_check_dts_bounds(&dts)
746751

747-
# set tz if needed
748-
if _tzinfo is not None:
749-
value = tz_convert_single(value, _tzinfo, 'UTC')
750-
751-
result = create_timestamp_from_ts(value, dts, _tzinfo, self.freq)
752-
return result
752+
return create_timestamp_from_ts(value, dts, _tzinfo, self.freq)
753753

754754
def isoformat(self, sep='T'):
755755
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)