Skip to content

Commit bc9d329

Browse files
AlexKirkomroeschke
authored andcommitted
BUG: Fix Timestamp constructor changes value on ambiguous DST (#30995)
* BUG: fix Timestamp constructor value change on DST Timestamp * BUG: expand if check for Linux * CLN: switch from isinstance to treat_tz_asdateutil * CLN: add comment to solution and move to v1.1.0 * DOC: reword bugfix comment
1 parent 9a68635 commit bc9d329

File tree

4 files changed

+22
-8
lines changed

4 files changed

+22
-8
lines changed

doc/source/whatsnew/v1.1.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ Categorical
5959

6060
Datetimelike
6161
^^^^^^^^^^^^
62+
- Bug in :class:`Timestamp` where constructing :class:`Timestamp` from ambiguous epoch time and calling constructor again changed :meth:`Timestamp.value` property (:issue:`24329`)
6263
-
6364
-
6465

pandas/_libs/tslibs/conversion.pyx

+9-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ from pandas._libs.tslibs.util cimport (
2929
from pandas._libs.tslibs.timedeltas cimport cast_from_unit
3030
from pandas._libs.tslibs.timezones cimport (
3131
is_utc, is_tzlocal, is_fixed_offset, get_utcoffset, get_dst_info,
32-
get_timezone, maybe_get_tz, tz_compare)
32+
get_timezone, maybe_get_tz, tz_compare, treat_tz_as_dateutil)
3333
from pandas._libs.tslibs.timezones import UTC
3434
from pandas._libs.tslibs.parsing import parse_datetime_string
3535

@@ -362,6 +362,14 @@ cdef _TSObject convert_datetime_to_tsobject(datetime ts, object tz,
362362
obj.tzinfo = tz
363363
else:
364364
obj.value = pydatetime_to_dt64(ts, &obj.dts)
365+
# GH 24329 When datetime is ambiguous,
366+
# pydatetime_to_dt64 doesn't take DST into account
367+
# but with dateutil timezone, get_utcoffset does
368+
# so we need to correct for it
369+
if treat_tz_as_dateutil(ts.tzinfo):
370+
if ts.tzinfo.is_ambiguous(ts):
371+
dst_offset = ts.tzinfo.dst(ts)
372+
obj.value += int(dst_offset.total_seconds() * 1e9)
365373
obj.tzinfo = ts.tzinfo
366374

367375
if obj.tzinfo is not None and not is_utc(obj.tzinfo):

pandas/tests/indexes/datetimes/test_timezones.py

+1-7
Original file line numberDiff line numberDiff line change
@@ -573,13 +573,7 @@ def test_dti_construction_ambiguous_endpoint(self, tz):
573573
"2013-10-26 23:00", "2013-10-27 01:00", freq="H", tz=tz, ambiguous="infer"
574574
)
575575
assert times[0] == Timestamp("2013-10-26 23:00", tz=tz, freq="H")
576-
577-
if str(tz).startswith("dateutil"):
578-
# fixed ambiguous behavior
579-
# see GH#14621
580-
assert times[-1] == Timestamp("2013-10-27 01:00:00+0100", tz=tz, freq="H")
581-
else:
582-
assert times[-1] == Timestamp("2013-10-27 01:00:00+0000", tz=tz, freq="H")
576+
assert times[-1] == Timestamp("2013-10-27 01:00:00+0000", tz=tz, freq="H")
583577

584578
@pytest.mark.parametrize(
585579
"tz, option, expected",

pandas/tests/scalar/timestamp/test_timestamp.py

+11
Original file line numberDiff line numberDiff line change
@@ -1081,3 +1081,14 @@ def test_dt_subclass_add_timedelta(lh, rh):
10811081
result = lh + rh
10821082
expected = SubDatetime(2000, 1, 1, 1)
10831083
assert result == expected
1084+
1085+
1086+
def test_constructor_ambigous_dst():
1087+
# GH 24329
1088+
# Make sure that calling Timestamp constructor
1089+
# on Timestamp created from ambiguous time
1090+
# doesn't change Timestamp.value
1091+
ts = Timestamp(1382835600000000000, tz="dateutil/Europe/London")
1092+
expected = ts.value
1093+
result = Timestamp(ts).value
1094+
assert result == expected

0 commit comments

Comments
 (0)