Subject: [PATCH] Implement LastNDayOfMonthExpression --- Index: tests/triggers/test_cron.py IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/tests/triggers/test_cron.py b/tests/triggers/test_cron.py --- a/tests/triggers/test_cron.py (revision ea3c6476b3cb5e1821ed8229c63a56e5d60c5bee) +++ b/tests/triggers/test_cron.py (date 1731599053592) @@ -160,6 +160,22 @@ ) +def test_cron_trigger_5(timezone, serializer): + start_time = datetime(2012, 2, 1, tzinfo=timezone) + trigger = CronTrigger( + year="2012", month="2", day="last-6", start_time=start_time, timezone=timezone + ) + if serializer: + trigger = serializer.deserialize(serializer.serialize(trigger)) + + assert trigger.next() == datetime(2012, 2, 23, tzinfo=timezone) + assert repr(trigger) == ( + "CronTrigger(year='2012', month='2', day='last-6', week='*', " + "day_of_week='*', hour='0', minute='0', second='0', " + "start_time='2012-02-01T00:00:00+01:00', timezone='Europe/Berlin')" + ) + + @pytest.mark.parametrize("expr", ["3-5", "wed-fri"], ids=["numeric", "text"]) def test_weekday_overlap(timezone, serializer, expr): start_time = datetime(2009, 1, 1, tzinfo=timezone) Index: src/apscheduler/triggers/cron/expressions.py IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/src/apscheduler/triggers/cron/expressions.py b/src/apscheduler/triggers/cron/expressions.py --- a/src/apscheduler/triggers/cron/expressions.py (revision ea3c6476b3cb5e1821ed8229c63a56e5d60c5bee) +++ b/src/apscheduler/triggers/cron/expressions.py (date 1731599053542) @@ -251,3 +251,20 @@ def __str__(self) -> str: return "last" + + +class LastNDayOfMonthExpression(AllExpression): + value_re = re.compile(r"last-(?P[0-9]+)", re.IGNORECASE) + + def __init__(self, last_day): + super(LastNDayOfMonthExpression, self).__init__(None) + self.last_day = as_int(last_day) + + def get_next_value(self, date, field): + currval = field.get_value(date) + nextval = monthrange(date.year, date.month)[1]-self.last_day + + return nextval if currval <= nextval else None + + def __str__(self): + return f"last-{self.last_day}" \ No newline at end of file Index: src/apscheduler/triggers/cron/fields.py IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/src/apscheduler/triggers/cron/fields.py b/src/apscheduler/triggers/cron/fields.py --- a/src/apscheduler/triggers/cron/fields.py (revision ea3c6476b3cb5e1821ed8229c63a56e5d60c5bee) +++ b/src/apscheduler/triggers/cron/fields.py (date 1731599053562) @@ -13,6 +13,7 @@ from .expressions import ( WEEKDAYS, AllExpression, + LastNDayOfMonthExpression, LastDayOfMonthExpression, MonthRangeExpression, RangeExpression, @@ -122,7 +123,7 @@ class DayOfMonthField( - BaseField, extra_compilers=(WeekdayPositionExpression, LastDayOfMonthExpression) + BaseField, extra_compilers=(WeekdayPositionExpression, LastNDayOfMonthExpression, LastDayOfMonthExpression) ): __slots__ = ()