Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion pandas/core/arrays/datetimelike.py
Original file line number Diff line number Diff line change
Expand Up @@ -2383,7 +2383,24 @@ def _concat_same_type(

if obj.freq is not None and all(x.freq == obj.freq for x in to_concat):
pairs = zip(to_concat[:-1], to_concat[1:], strict=True)
if all(pair[0][-1] + obj.freq == pair[1][0] for pair in pairs):
# GH#62915: For timezone-aware datetimes with fixed frequencies,
# DST transitions can cause naive addition (pair[0][-1] + freq) to
# not equal pair[1][0] even when they're legitimately consecutive.
# For Tick frequencies, compare using underlying int64 values.
# For non-Tick frequencies, use the original comparison.
try:
freq_nanos = obj.freq.nanos
pairs_match = all(
pair[1][0]._value - pair[0][-1]._value == freq_nanos
for pair in pairs
)
except (ValueError, AttributeError):
# Non-fixed frequency, fall back to original comparison
pairs_match = all(
pair[0][-1] + obj.freq == pair[1][0] for pair in pairs
)

if pairs_match:
new_freq = obj.freq
new_obj._freq = new_freq
return new_obj
Expand Down
14 changes: 14 additions & 0 deletions pandas/tests/indexes/datetimes/test_setops.py
Original file line number Diff line number Diff line change
Expand Up @@ -729,6 +729,20 @@ def test_intersection_dst_transition(self, tz):
expected = date_range("2021-10-28", periods=6, freq="D", tz="Europe/London")
tm.assert_index_equal(result, expected)

def test_union_dst_boundary(self):
# GH#62915: index.union fails at DST boundary
# When one index ends at DST transition and the other crosses it,
# the union should succeed and preserve frequency
index1 = date_range("2025-10-25", "2025-10-26", freq="D", tz="Europe/Helsinki")
index2 = date_range("2025-10-25", "2025-10-28", freq="D", tz="Europe/Helsinki")

result = index1.union(index2)
expected = date_range(
"2025-10-25", "2025-10-28", freq="D", tz="Europe/Helsinki"
)
tm.assert_index_equal(result, expected)
assert result.freq == expected.freq


def test_union_non_nano_rangelike():
# GH 59036
Expand Down
Loading