diff --git a/pandas/core/arrays/_ranges.py b/pandas/core/arrays/_ranges.py index 48ca71c0c11e4..878bf34bcdd1d 100644 --- a/pandas/core/arrays/_ranges.py +++ b/pandas/core/arrays/_ranges.py @@ -152,7 +152,13 @@ def generate_daily_offset_range( i8values = i8values[freq._get_daily_offset_mask(dt64)] # type: ignore[attr-defined] if abs_n > 1: - i8values = i8values[::abs_n] + # When trimming from the end (end+periods case), anchor stride on + # the last on-offset date so e.g. freq="2B" with end on a Sunday + # yields the last business day, last-2, last-4, ... (GH#64834). + if trim_from_end: + i8values = i8values[::-1][::abs_n][::-1] + else: + i8values = i8values[::abs_n] if trim_to is not None: if trim_from_end: diff --git a/pandas/tests/indexes/datetimes/test_date_range.py b/pandas/tests/indexes/datetimes/test_date_range.py index 8c52d2c7a71a6..9a31e01da2152 100644 --- a/pandas/tests/indexes/datetimes/test_date_range.py +++ b/pandas/tests/indexes/datetimes/test_date_range.py @@ -1194,6 +1194,19 @@ def test_bdate_range_end_weekend_periods(self): result = bdate_range(end="2026-03-22", periods=3) tm.assert_index_equal(result, expected) + @pytest.mark.parametrize("end", ["2026-03-20", "2026-03-21", "2026-03-22"]) + def test_bdate_range_end_periods_multiple_n(self, end): + # GH#64648 (post-merge): with end+periods and freq="nB" (n>=2), + # the on-offset stride must be anchored at the end (last business + # day <= end), not at the start of the internal buffer. + result = bdate_range(end=end, periods=3, freq="2B") + expected = DatetimeIndex(["2026-03-16", "2026-03-18", "2026-03-20"]) + tm.assert_index_equal(result, expected) + + result = bdate_range(end=end, periods=3, freq="3B") + expected = DatetimeIndex(["2026-03-12", "2026-03-17", "2026-03-20"]) + tm.assert_index_equal(result, expected) + class TestCustomDateRange: def test_constructor(self):