Skip to content

phil/recurrence

alpha

Repeating dates from an RFC 5545 rule: read it, check it, write it, list its occurrences.

phil/recurrence reads the RRULEs of iCalendar: yearly, monthly, weekly and daily, every so many periods, by weekday with its place in the month or year, by month day from the start or the end, by month and by set position, until a date or a number of times. It checks a rule against the RFC, writes it back as text, and expands it into occurrences, with the dates RDATE adds and without those its EXDATE exceptions take away, and reads and writes their lines. It can also describe a recurrence as a schema.org Schedule. It does not read hourly or finer frequencies, week numbers or year days, nor periods of RDATE.

It is for any PHP application whose events repeat: a weekly meeting, a monthly club night, a yearly festival. Rules follow the RFC so that they travel to and from other calendars unchanged. The library is written from scratch from RFC 5545 and depends on nothing but PHP and ext-mbstring. It is not a calendar: phil/calendar-bundle is one, built on it. In a Symfony application, phil/recurrence-bundle adds a form, a Doctrine column type and Twig functions around it.

The design rests on two value types. RecurrenceRule holds a rule and refuses one the RFC does not allow. Recurrence pairs a rule with a first occurrence and gives the occurrences in that occurrence's time zone, keeping its wall-clock time across changes of time.

Status

PartState
RecurrenceRule: reading, checking against RFC 5545, writingImplemented and tested
Recurrence: occurrences, between moments, next one, exceptionsImplemented and tested
RDATE merged into the occurrences outside COUNT; RecurrenceDates: RDATE and EXDATE lines read and writtenImplemented and tested
The examples of RFC 5545, section 3.8.5.3, that the library readsImplemented and tested

Documentation

DocumentWhat it covers
InstallationRequirements and installing.
RulesRecurrenceRule, its parts, what is checked, what is not read.
OccurrencesRecurrence, time zones, ends, exceptions, added dates.
Added and removed datesRecurrenceDates, the RDATE and EXDATE lines read and written.
Extension pointsThe ports, arguments and data an application chooses or replaces.
TestingThe library's tests and mutation bar.
ReferenceEvery type of the library, with its role.

Quick start

composer require phil/recurrence
use Phil\Recurrence\Recurrence;
use Phil\Recurrence\RecurrenceRule;

$rule = RecurrenceRule::parse('FREQ=MONTHLY;BYDAY=2TU;COUNT=6');
$meeting = Recurrence::of(new DateTimeImmutable('2026-11-10 19:00', new DateTimeZone('America/Toronto')), $rule);

$meeting->between(limit: 3);                  // 2026-11-10, 2026-12-08, 2027-01-12, at 19:00 in Toronto
$meeting->next(new DateTimeImmutable());      // the next one, or null

Requirements

PHP 8.5 or later with ext-mbstring.

Contributing

Contributions are welcome on the PHIL monorepo: read the contribution guide and the code of conduct. Report a vulnerability privately, as the security policy explains, never in a public issue.

License

MIT, © Philippe Gamache, doing business as Le Gnome Archiviste; a Parler Haut Interagir Librement project. See LICENSE.