Recurr is a PHP library for working with recurrence rules (RRULE) and converting them in to DateTime objects.
Recurr was developed as a precursor for a calendar with recurring events, and is heavily inspired by rrule.js.
Installing Recurr
The recommended way to install Recurr is through Composer.
composer require simshaun/recurr
Using Recurr
Creating RRULE rule objects
You can create a new Rule object by passing the (RRULE) string or an array with the rule parts, the start date, end date (optional) and timezone.
$timezone = 'America/New_York';
$startDate = new \DateTime('2013-06-12 20:00:00', new \DateTimeZone($timezone));
$endDate = new \DateTime('2013-06-14 20:00:00', new \DateTimeZone($timezone)); // Optional
$rule = new \Recurr\Rule('FREQ=MONTHLY;COUNT=5', $startDate, $endDate, $timezone);
You can also use chained methods to build your rule programmatically and get the resulting RRULE.
$rule = (new \Recurr\Rule)
->setStartDate($startDate)
->setTimezone($timezone)
->setFreq('DAILY')
->setByDay(['MO', 'TU'])
->setUntil(new \DateTime('2017-12-31'))
;
echo $rule->getString(); //FREQ=DAILY;UNTIL=20171231T000000;BYDAY=MO,TU
RRULE to DateTime objects
$transformer = new \Recurr\Transformer\ArrayTransformer();
print_r($transformer->transform($rule));
$transformer->transform(...)
returns aRecurrenceCollection
ofRecurrence
objects.- Each
Recurrence
hasgetStart()
andgetEnd()
methods that return a\DateTime
object. - If the transformed
Rule
lacks an end date,getEnd()
will return a\DateTime
object equal to that ofgetStart()
.
Note: The transformer has a "virtual" limit (default 732) on the number of objects it generates. This prevents the script from crashing on an infinitely recurring rule. You can change the virtual limit with an
ArrayTransformerConfig
object that you pass toArrayTransformer
.
Transformation Constraints
Constraints are used by the ArrayTransformer to allow or prevent certain dates from being added to a RecurrenceCollection
. Recurr provides the following constraints:
AfterConstraint(\DateTime $after, $inc = false)
BeforeConstraint(\DateTime $before, $inc = false)
BetweenConstraint(\DateTime $after, \DateTime $before, $inc = false)
$inc
defines what happens if $after
or $before
are themselves recurrences. If $inc = true
, they will be included in the collection. For example,
$startDate = new \DateTime('2014-06-17 04:00:00');
$rule = new \Recurr\Rule('FREQ=MONTHLY;COUNT=5', $startDate);
$transformer = new \Recurr\Transformer\ArrayTransformer();
$constraint = new \Recurr\Transformer\Constraint\BeforeConstraint(new \DateTime('2014-08-01 00:00:00'));
print_r($transformer->transform($rule, $constraint));
Note: If building your own constraint, it is important to know that dates which do not meet the constraint's requirements do not count toward the transformer's virtual limit. If you manually set your constraint's
$stopsTransformer
property tofalse
, the transformer might crash via an infinite loop. See theBetweenConstraint
for an example on how to prevent that.
RecurrenceCollection
Filters
Post-Transformation RecurrenceCollection
provides the following chainable helper methods to filter out recurrences:
startsBetween(\DateTime $after, \DateTime $before, $inc = false)
startsBefore(\DateTime $before, $inc = false)
startsAfter(\DateTime $after, $inc = false)
endsBetween(\DateTime $after, \DateTime $before, $inc = false)
endsBefore(\DateTime $before, $inc = false)
endsAfter(\DateTime $after, $inc = false)
$inc
defines what happens if $after
or $before
are themselves recurrences. If $inc = true
, they will be included in the filtered collection. For example,
pseudo...
2014-06-01 startsBetween(2014-06-01, 2014-06-20) // false
2014-06-01 startsBetween(2014-06-01, 2014-06-20, true) // true
Note:
RecurrenceCollection
extends the Doctrine project's ArrayCollection class.
RRULE to Text
Recurr supports transforming some recurrence rules into human readable text. This feature is still in beta and only supports yearly, monthly, weekly, and daily frequencies.
$rule = new Rule('FREQ=YEARLY;INTERVAL=2;COUNT=3;', new \DateTime());
$textTransformer = new TextTransformer();
echo $textTransformer->transform($rule);
If you need more than English you can pass in a translator with one of the supported locales (see translations folder).
$rule = new Rule('FREQ=YEARLY;INTERVAL=2;COUNT=3;', new \DateTime());
$textTransformer = new TextTransformer(
new \Recurr\Transformer\Translator('de')
);
echo $textTransformer->transform($rule);
Warnings
- **Monthly recurring rules ** By default, if your start date is on the 29th, 30th, or 31st, Recurr will skip following months that don't have at least that many days. (e.g. Jan 31 + 1 month = March)
This behavior is configurable:
$timezone = 'America/New_York';
$startDate = new \DateTime('2013-01-31 20:00:00', new \DateTimeZone($timezone));
$rule = new \Recurr\Rule('FREQ=MONTHLY;COUNT=5', $startDate, null, $timezone);
$transformer = new \Recurr\Transformer\ArrayTransformer();
$transformerConfig = new \Recurr\Transformer\ArrayTransformerConfig();
$transformerConfig->enableLastDayOfMonthFix();
$transformer->setConfig($transformerConfig);
print_r($transformer->transform($rule));
// 2013-01-31, 2013-02-28, 2013-03-31, 2013-04-30, 2013-05-31
Contribute
Feel free to comment or make pull requests. Please include tests with PRs.
License
Recurr is licensed under the MIT License. See the LICENSE file for details.