--- title: STARTS_ON, ENDS_ON and ENDS_AFTER rules description: Giving a repeating pattern its own start, end and count, so a hand-picked day can fall outside it without moving the pattern. order: 5 --- # STARTS_ON, ENDS_ON and ENDS_AFTER rules Format: ```text { {rules…}, STARTS_ON(YYYY-MM-DD) } { {rules…}, ENDS_ON(YYYY-MM-DD) } { {rules…}, ENDS_AFTER(N) } { {rules…}, STARTS_ON(YYYY-MM-DD), ENDS_ON(YYYY-MM-DD), ENDS_AFTER(N) } ``` `STARTS_ON` gives the pattern inside a brace group its own start date. Everything else in the group takes its phase and its floor from that date instead of the event's start date. `ENDS_ON` is the last day the group's pattern may fall on, inclusive, and `ENDS_AFTER` stops it after its Nth day. All three are modifiers, not rules: they produce no dates and remove none. ## Why it exists An event's start date phases its pattern (`5Month2` counts every second month from it) and floors it (nothing falls before it). Tap a day on the calendar *before* the pattern begins and the two jobs collide: the series must now start on the tapped day, but the pattern must not move with it. ```text {5Month2, STARTS_ON(2024-02-05)}, 2024-01-10 ``` → from an event that starts on 1 January 2024: 10 January once, then the 5th of February, April and June. Without the anchor the pattern would count from January and land on the 5th of January, March and May. The same happens at the far end. An event's end date caps everything in it, so a day tapped after the pattern ends is clipped unless the end date moves, and moving it lets the pattern run on to fill the gap: ```text {Mon, ENDS_ON(2024-01-15)}, 2024-02-05 ``` → from an event that starts on 2 January 2024: Mondays on 8 and 15 January, then 5 February once. A count works the same way. An event that ends after N occurrences counts every day it is on, hand-picked ones included, so a day picked after the Nth was clipped and one picked before it pushed the pattern's last day out. `ENDS_AFTER` counts the group's own days: ```text {Mon, ENDS_AFTER(2)}, 2024-03-04 ``` → from an event that starts on 1 January 2024: Mondays on 1 and 8 January, then 4 March once. The count is of what the group produces after its own exclusions and limits, so an excluded Monday inside the group does not use one of the N. A day removed outside the group does. ## Rules - Inside `{ }` only. On their own at the top level they are rejected. - One of each per group. A group may carry all three; with both an `ENDS_ON` and an `ENDS_AFTER`, whichever comes first wins. - Never negated: `!STARTS_ON(...)`, `!ENDS_ON(...)` and `!ENDS_AFTER(...)` are rejected. - Not the normal rule a group has to have: `{STARTS_ON(2024-01-15)}` is rejected. - A real date: `STARTS_ON(2024-02-30)` is rejected. A real count: `ENDS_AFTER(0)` is rejected. - Never ending before starting: `{Mon, STARTS_ON(2024-02-05), ENDS_ON(2024-01-15)}` is rejected. The same day for both is a one-day group, and allowed. - Cannot be shifted (`+P`/`-P`) or adjusted around (`~`), like `LIMIT`. - A `LIMIT` or an exclusion in the same group is scoped to the group exactly as it would be without the bounds. ## Examples ```text {Mon, STARTS_ON(2024-01-15)} Mondays from 15 January 2024 {Mon2, STARTS_ON(2024-01-08)} every second Monday, counted from 8 January 2024 {Day, LIMIT(Feb), STARTS_ON(2024-02-27)} 27, 28, 29 February 2024, then every February {5Month2, STARTS_ON(2024-02-05)}, 2024-01-10 the 5th of every other month from February, plus 10 January {Mon, ENDS_ON(2024-01-15)} Mondays up to and including 15 January 2024 {Mon, ENDS_ON(2024-01-15)}, 2024-02-05 …plus 5 February once {5Month2, STARTS_ON(2024-02-05), ENDS_ON(2024-05-31)} 5 February and 5 April 2024, and that is all {Mon, ENDS_AFTER(4)} the first four Mondays {Mon, !2024-01-08, ENDS_AFTER(4)} four Mondays, skipping 8 January: 1, 15, 22, 29 January {Mon, STARTS_ON(2024-01-15), ENDS_AFTER(3)} three Mondays from 15 January ``` ## You never have to type it The app writes them. Tap a day before a repeating event begins and the pattern is wrapped with its old start date as the anchor, the event's start date moves back to the tapped day, and the pattern carries on exactly as it was. Tap a day after it ends, whether on a date or after so many times, and the pattern is capped at its old end while the event's end date moves on. Tap the day off again and the wrapping goes away. The Repeat sheet shows the pattern without any of them, and reads "After N" back from the count. ## Export A group ended by `ENDS_ON` or `ENDS_AFTER` exports as the rule it holds, with `UNTIL` or `COUNT` on the `RRULE`; a day picked after the end is an ordinary `RDATE`. `COUNT` is only used where it means the same in both worlds, a lone group with nothing removed in or around it and no end on the series, because RFC 5545 counts before `EXDATE` removes; otherwise the group's last day becomes `UNTIL`. An `RRULE` is phased by `DTSTART`, so a group anchored by `STARTS_ON` later than the event's first day has no `RRULE` form and its dates are written out to the export horizon instead, like `MOON` and `EASTER`. `UNTIL` and `COUNT` read back on import as the series' end date and count, as they always did; the importer does not yet read `RDATE`s. See the [rule reference](/docs/rules-reference.html) for the surrounding syntax.