recurr

repository·master·Indexed 23 days ago

https://github.com/simshaun/recurr

A PHP library for parsing and working with RFC 5545 recurrence rules (RRULEs). It allows developers to convert these rules into collections of DateTime objects or human-readable text. Key features include the Rule class for managing RRULE configurations, the ArrayTransformer for expanding rules into RecurrenceCollections, and the TextTransformer for generating human-readable strings.

Tokens
4.9K
Snippets
7
Records
32
Agent score
82%

What's inside recurr

  1. Convert RRULEs to DateTime objects

    master

    To expand an RRULE into actual occurrences, use the \Recurr\Transformer\ArrayTransformer.

    1. Call transform($rule) on the transformer. It returns a RecurrenceCollection containing Recurrence objects.
    2. Each Recurrence object provides getStart() and getEnd() methods, which return \DateTime objects.
    3. If the rule does not specify an end date, getEnd() returns a \DateTime object equal to the start date.

    Note on Limits: The transformer has a default virtual limit of 732 objects to prevent infinite loops from infinite rules. You can adjust this limit by passing an ArrayTransformerConfig object to the ArrayTransformer via setConfig().

    $transformer = new \Recurr\Transformer\ArrayTransformer();
    
    print_r($transformer->transform($rule));
  2. Configure Monthly Last-Day-of-Month behavior

    master

    By default, if a monthly rule starts on the 29th, 30th, or 31st, Recurr skips months that do not contain that day (e.g., Jan 31st + 1 month = March).

    To fix this so that it uses the last available day of the month (e.g., Jan 31st + 1 month = Feb 28th), use ArrayTransformerConfig::enableLastDayOfMonthFix() and pass it to the transformer.

    $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
  3. Upgrade to Recurr v6.0.0

    master
    When upgrading to version 6.0.0, be aware of several breaking changes related to PHP version requirements and strict typing. While the core logic for RRULE parsing, transformations, and date calculations remains unchanged, you must ensure your environment and custom implementations comply with the new type safety standards.
  4. Use positional BYDAY values

    master

    The setByDay() method supports positional values (e.g., +1MO for the first Monday of the month, or -1FR for the last Friday).

    Constraints:

    1. Frequency Requirement: Positional BYDAY values only work with MONTHLY or YEARLY frequencies.
    2. No Mixing: You cannot mix positional (e.g., -1MO) and non-positional (e.g., MO) values in the same BYDAY array.
  5. Handle Strict Type Declarations in Recurr v6.0.0

    master

    Recurr v6.0.0 introduces strict type declarations for all method parameters and return types.

    Impact: If your code relied on loose type coercion when interacting with Recurr, it may now throw exceptions.

    Action Required: Review any code that extends Recurr classes or heavily utilizes them to ensure your data types are compatible with the new strict signatures.

  6. Transform RRULE to human-readable text

    master

    The TextTransformer (currently in beta) converts RRULEs into human-readable strings. It currently supports YEARLY, MONTHLY, WEEKLY, and DAILY frequencies.

    You can localize the output by passing a \Recurr\Transformer\Translator with a specific locale to the TextTransformer constructor.

    // English (default)
    $rule = new Rule('FREQ=YEARLY;INTERVAL=2;COUNT=3;', new \DateTime());
    $textTransformer = new TextTransformer();
    echo $textTransformer->transform($rule);
    
    // German
    $textTransformer = new TextTransformer(
        new \Recurr\Transformer\Translator('de')
    );
    echo $textTransformer->transform($rule);
  7. Update Custom TranslatorInterface Implementations for v6.0.0

    master

    If you have implemented a custom TranslatorInterface, you must update the trans method signature to match the new requirements in v6.0.0. The method now requires strict type hints for the string parameter, an optional params array, and an explicit return type of string|array.

    interface TranslatorInterface
    {
        public function trans(string $string, array $params = []): string|array;
    }
  8. Apply Transformation Constraints

    master

    Constraints allow you to limit the dates generated by the ArrayTransformer during the transformation process. This is more efficient than filtering after the fact because dates that don't meet the constraint do not count toward the transformer's virtual limit.

    Supported constraints:

    • AfterConstraint(\DateTime $after, $inc = false)
    • BeforeConstraint(\DateTime $before, $inc = false)
    • BetweenConstraint(\DateTime $after, \DateTime $before, $inc = false)

    The $inc parameter determines if the boundary date itself is included in the collection.

    $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));
  9. Filter a RecurrenceCollection

    master

    A RecurrenceCollection (which extends Doctrine's ArrayCollection) provides chainable helper methods to filter occurrences after they have been transformed. Use these to narrow down results based on start or end times.

    Available filters:

    • 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)

    The $inc parameter determines if the boundary date is included.

  10. Create RRULE rule objects

    master

    You can instantiate a \Recurr\Rule object in two ways:

    1. Constructor: Pass an RRULE string (or an array of rule parts), a start date, an optional end date, and a timezone.
    2. Fluent Interface: Use chained setter methods to build the rule programmatically.

    Use getString() to retrieve the resulting RRULE string.

    $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);
    
    // Or using chained methods
    $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