Skip to content

Commit

Permalink
BUG: Bad shift_forward around UTC+0 when DST (#56017)
Browse files Browse the repository at this point in the history
* add tests

* fix

* add whatsnew

* refactor

* Revert "refactor"

This reverts commit 4482dd8.

* fix bug

* update

* more cmt
  • Loading branch information
quangngd authored Nov 21, 2023
1 parent 1e1f53c commit df6a323
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 3 deletions.
2 changes: 1 addition & 1 deletion doc/source/whatsnew/v2.2.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@ Timezones
^^^^^^^^^
- Bug in :class:`AbstractHolidayCalendar` where timezone data was not propagated when computing holiday observances (:issue:`54580`)
- Bug in :class:`Timestamp` construction with an ambiguous value and a ``pytz`` timezone failing to raise ``pytz.AmbiguousTimeError`` (:issue:`55657`)
-
- Bug in :meth:`Timestamp.tz_localize` with ``nonexistent="shift_forward`` around UTC+0 during DST (:issue:`51501`)

Numeric
^^^^^^^
Expand Down
9 changes: 7 additions & 2 deletions pandas/_libs/tslibs/tzconversion.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -416,8 +416,13 @@ timedelta-like}

else:
delta_idx = bisect_right_i8(info.tdata, new_local, info.ntrans)

delta_idx = delta_idx - delta_idx_offset
# Logic similar to the precompute section. But check the current
# delta in case we are moving between UTC+0 and non-zero timezone
if (shift_forward or shift_delta > 0) and \
info.deltas[delta_idx - 1] >= 0:
delta_idx = delta_idx - 1
else:
delta_idx = delta_idx - delta_idx_offset
result[i] = new_local - info.deltas[delta_idx]
elif fill_nonexist:
result[i] = NPY_NAT
Expand Down
38 changes: 38 additions & 0 deletions pandas/tests/scalar/timestamp/methods/test_tz_localize.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,44 @@ def test_tz_localize_nonexistent(self, stamp, tz):
ts.tz_localize(tz, nonexistent="raise")
assert ts.tz_localize(tz, nonexistent="NaT") is NaT

@pytest.mark.parametrize(
"stamp, tz, forward_expected, backward_expected",
[
(
"2015-03-29 02:00:00",
"Europe/Warsaw",
"2015-03-29 03:00:00",
"2015-03-29 01:59:59",
), # utc+1 -> utc+2
(
"2023-03-12 02:00:00",
"America/Los_Angeles",
"2023-03-12 03:00:00",
"2023-03-12 01:59:59",
), # utc-8 -> utc-7
(
"2023-03-26 01:00:00",
"Europe/London",
"2023-03-26 02:00:00",
"2023-03-26 00:59:59",
), # utc+0 -> utc+1
(
"2023-03-26 00:00:00",
"Atlantic/Azores",
"2023-03-26 01:00:00",
"2023-03-25 23:59:59",
), # utc-1 -> utc+0
],
)
def test_tz_localize_nonexistent_shift(
self, stamp, tz, forward_expected, backward_expected
):
ts = Timestamp(stamp)
forward_ts = ts.tz_localize(tz, nonexistent="shift_forward")
assert forward_ts == Timestamp(forward_expected, tz=tz)
backward_ts = ts.tz_localize(tz, nonexistent="shift_backward")
assert backward_ts == Timestamp(backward_expected, tz=tz)

def test_tz_localize_ambiguous_raise(self):
# GH#13057
ts = Timestamp("2015-11-1 01:00")
Expand Down

0 comments on commit df6a323

Please sign in to comment.