From 554227c13efdd4a177dd97753be1fb8895a07ca4 Mon Sep 17 00:00:00 2001 From: Sskift <163287349+Sskift@users.noreply.github.com> Date: Tue, 3 Nov 2026 01:42:05 +0800 Subject: [PATCH] Reject nonpositive weekday occurrences --- src/pendulum/date.py | 6 ++++-- src/pendulum/datetime.py | 5 ++++- tests/date/test_day_of_week_modifiers.py | 10 ++++++++++ 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/pendulum/date.py b/src/pendulum/date.py index cd703d2e..61a7354d 100644 --- a/src/pendulum/date.py +++ b/src/pendulum/date.py @@ -550,13 +550,15 @@ def nth_of(self, unit: str, nth: int, day_of_week: WeekDay) -> Self: Supported units are month, quarter and year. :param unit: The unit to use - :param nth: The occurrence to use + :param nth: The occurrence to use, starting at 1 :param day_of_week: The day of week to set to. """ if unit not in ["month", "quarter", "year"]: raise ValueError(f'Invalid unit "{unit}" for first_of()') - dt = cast("Self", getattr(self, f"_nth_of_{unit}")(nth, day_of_week)) + dt = None + if nth > 0: + dt = cast("Self", getattr(self, f"_nth_of_{unit}")(nth, day_of_week)) if not dt: raise PendulumException( f"Unable to find occurrence {nth}" diff --git a/src/pendulum/datetime.py b/src/pendulum/datetime.py index 96aff25b..281456f0 100644 --- a/src/pendulum/datetime.py +++ b/src/pendulum/datetime.py @@ -1005,11 +1005,14 @@ def nth_of(self, unit: str, nth: int, day_of_week: WeekDay) -> Self: to indicate the desired day_of_week, ex. DateTime.MONDAY. Supported units are month, quarter and year. + Occurrences start at 1. """ if unit not in ["month", "quarter", "year"]: raise ValueError(f'Invalid unit "{unit}" for first_of()') - dt = cast("Self | None", getattr(self, f"_nth_of_{unit}")(nth, day_of_week)) + dt = None + if nth > 0: + dt = cast("Self | None", getattr(self, f"_nth_of_{unit}")(nth, day_of_week)) if not dt: raise PendulumException( f"Unable to find occurrence {nth}" diff --git a/tests/date/test_day_of_week_modifiers.py b/tests/date/test_day_of_week_modifiers.py index b8286af6..a008e6b8 100644 --- a/tests/date/test_day_of_week_modifiers.py +++ b/tests/date/test_day_of_week_modifiers.py @@ -196,6 +196,16 @@ def test_nth_of_month_outside_scope(): d.nth_of("month", 6, pendulum.MONDAY) +@pytest.mark.parametrize("date_type", [pendulum.Date, pendulum.DateTime]) +@pytest.mark.parametrize("unit", ["month", "quarter", "year"]) +@pytest.mark.parametrize("nth", [0, -1, -4]) +def test_nth_of_nonpositive_occurrence(date_type, unit, nth): + d = date_type(2024, 5, 15) + + with pytest.raises(PendulumException): + d.nth_of(unit, nth, pendulum.FRIDAY) + + def test_nth_of_month_outside_year(): d = pendulum.date(1975, 12, 5)