PHPackages                             tplaner/when - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. tplaner/when

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

tplaner/when
============

Date/Calendar recursion library.

v3.2.1(1y ago)5331.1M↓12.3%91[2 issues](https://github.com/tplaner/When/issues)4MITPHPPHP &gt;=7.1.3|^8.0CI failing

Since Mar 7Pushed 1y ago24 watchersCompare

[ Source](https://github.com/tplaner/When)[ Packagist](https://packagist.org/packages/tplaner/when)[ Docs](https://github.com/tplaner/When)[ RSS](/packages/tplaner-when/feed)WikiDiscussions master Synced 1w ago

READMEChangelogDependencies (1)Versions (13)Used By (4)

When
====

[](#when)

Date/Calendar recursion library for PHP 7.1+

[![Build Status](https://github.com/tplaner/When/actions/workflows/tests.yml/badge.svg)](https://github.com/tplaner/When/actions)[![Total Downloads](https://camo.githubusercontent.com/e0771e2300d698946d44838c14d59bbb0d8598ca70844b8152e858b7c2abace4/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f74706c616e65722f5768656e)](https://packagist.org/packages/tplaner/When)[![Latest Stable Version](https://camo.githubusercontent.com/4d8cbd3f83d8308b84cf3e8943a50f69e1b7220eb53481bf6b5f508638531ea1/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f74706c616e65722f5768656e)](https://packagist.org/packages/tplaner/When)[![License](https://camo.githubusercontent.com/faf221dfdab2fb6b3bf85f91537d0b5fa9412a98ac7f528407f370bd0ee8a7c5/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f74706c616e65722f5768656e)](https://packagist.org/packages/tplaner/When)

Author: [Tom Planer](https://twitter.com/tplaner)

Installation
------------

[](#installation)

```
composer require tplaner/When

```

Current Features
----------------

[](#current-features)

When offers full support for [RFC5455 Recurrence Rule](https://datatracker.ietf.org/doc/html/rfc5545#section-3.8.5) features (and some bonus features). Please check the [unit tests](https://github.com/tplaner/When/tree/master/tests) for information and examples about how to use it.

Here are some basic examples.

```
// friday the 13th for the next 5 occurrences
$r = new When();
$r->startDate(new DateTime("19980213T090000"))
  ->freq("monthly")
  ->count(5)
  ->byday("fr")
  ->bymonthday(13)
  ->generateOccurrences();

print_r($r->occurrences);
```

```
// friday the 13th for the next 5 occurrences rrule
$r = new When();
$r->startDate(new DateTime("19980213T090000"))
  ->rrule("FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13;COUNT=5")
  ->generateOccurrences();

print_r($r->occurrences);
```

```
// friday the 13th for the next 5 occurrences, skipping known friday the 13ths
$r = new When();
$r->startDate(new DateTime("19980213T090000"))
  ->freq("monthly")
  ->count(5)
  ->byday("fr")
  ->bymonthday(13)
  ->exclusions('19990813T090000,20001013T090000')
  ->generateOccurrences();

print_r($r->occurrences);
```

```
// friday the 13th forever; see which ones occur in 2018
$r = new When();
$r->startDate(new DateTime("19980213T090000"))
  ->rrule("FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13");

$occurrences = $r->getOccurrencesBetween(new DateTime('2018-01-01 09:00:00'),
                                         new DateTime('2019-01-01 09:00:00'));
print_r($occurrences);
```

InvalidStartDate Exception: The start date must be the first occurrence
-----------------------------------------------------------------------

[](#invalidstartdate-exception-the-start-date-must-be-the-first-occurrence)

According to [the specification](https://datatracker.ietf.org/doc/html/rfc5545) the starting date should be the first recurring date. This can often be troublesome, especially if you're generating the recurring dates from user input as it will throw an exception. You can disable this functionality easily:

```
$r = new When();
$r->RFC5545_COMPLIANT = When::IGNORE;

// get the last Friday of the month for the next 5 occurrences
$r->startDate(new DateTime())
  ->rrule("FREQ=MONTHLY;BYDAY=-1FR;COUNT=5")
  ->generateOccurrences();

print_r($r->occurrences);
```

Additional examples:
--------------------

[](#additional-examples)

```
$r = new When();
$r->RFC5545_COMPLIANT = When::IGNORE;

// second to last day of the month
$r->startDate(new DateTime())
  ->rrule("FREQ=MONTHLY;BYMONTHDAY=-2;COUNT=5")
  ->generateOccurrences();

print_r($r->occurrences);
```

```
$r = new When();
$r->RFC5545_COMPLIANT = When::IGNORE;

// every other week
$r->startDate(new DateTime())
    ->rrule("FREQ=WEEKLY;INTERVAL=2;COUNT=10")
    ->generateOccurrences();

print_r($r->occurrences);
```

```
$r1 = new When();
$r2 = new When();
$r1->RFC5545_COMPLIANT = When::IGNORE;
$r2->RFC5545_COMPLIANT = When::IGNORE;

// complex example of a payment schedule
// borrowed from: https://www.mikeyroy.com/2019/10/25/google-calendar-recurring-event-for-twice-monthly-payroll-only-on-weekdays/
//
// you're paid on the 15th, (or closest to it, but only on a weekday)
$r1->startDate(new DateTime())
   ->rrule("FREQ=MONTHLY;INTERVAL=1;BYSETPOS=-1;BYDAY=MO,TU,WE,TH,FR;BYMONTHDAY=13,14,15;COUNT=12")
   ->generateOccurrences();

// you're also paid on the last weekday of the month
$r2->startDate(new DateTime())
    ->rrule("FREQ=MONTHLY;INTERVAL=1;BYSETPOS=-1;BYDAY=MO,TU,WE,TH,FR;BYMONTHDAY=26,27,28,29,30,31;COUNT=12")
    ->generateOccurrences();

$totalPaydays = count($r1->occurrences);
for ($i = 0; $i < $totalPaydays; $i++)
{
    echo "You'll be paid on: " . $r1->occurrences[$i]->format('F d, Y') . "\n";
    echo "You'll be paid on: " . $r2->occurrences[$i]->format('F d, Y') . "\n";
}
```

Performance
-----------

[](#performance)

When is pretty fast, and shouldn't be able to loop infinitely. This is because the gregorian calendar actually repeats fully every 400 years. Therefore, this is an imposed upper limit to When, it will not generate occurrences more than 400 years into the future, and if it can't find a match in the next 400 years the pattern just doesn't exist.

By default, we do not generate more than 200 occurrences, though this can be configured simply by specifying a higher `COUNT` or by modifying the `$rangeLimit` prior to calling `generateOccurrences()`:

```
$r = new When();
$r->RFC5545_COMPLIANT = When::IGNORE;

$r->startDate(new DateTime())
  ->rrule("FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13")
  ->generateOccurrences();

// will generate an array of 200
print_r($r->occurrences);
```

The following is a pretty intensive benchmark the final occurrence is in the year 2254. On my machine this generates the results in about `0.28s`.

```
$r = new When();
$r->RFC5545_COMPLIANT = When::IGNORE;

$r->startDate(new DateTime(20210101))
  ->rrule("FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13;COUNT=400")
  ->generateOccurrences();

// will generate an array of 400
print_r($r->occurrences);
```

`COUNT` with an `UNTIL`, only 5 Friday the 13ths from 2021 to 2025.

```
$r = new When();
$r->RFC5545_COMPLIANT = When::IGNORE;

$r->startDate(new DateTime(20210101))
  ->rrule("FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13;COUNT=400;UNTIL=20250101")
  ->generateOccurrences();

// will generate until 2023-01-01 or 400
print_r($r->occurrences);
```

Limiting by `$rangeLimit`:

```
$r = new When();
$r->RFC5545_COMPLIANT = When::IGNORE;

$r->rangeLimit = 400;

$r->startDate(new DateTime())
  ->rrule("FREQ=MONTHLY;BYDAY=-1FR")
  ->generateOccurrences();

// 400 occurrences, limited by the rangeLimit
print_r($r->occurrences);
```

###  Health Score

57

—

FairBetter than 98% of packages

Maintenance42

Moderate activity, may be stable

Popularity61

Solid adoption and visibility

Community37

Small or concentrated contributor base

Maturity75

Established project with proven stability

 Bus Factor1

Top contributor holds 66.2% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~363 days

Recently: every ~315 days

Total

11

Last Release

481d ago

Major Versions

2.1.0 → v3.0.02019-04-17

PHP version history (3 changes)v2.0.0PHP &gt;=5.3.0

v3.0.0PHP &gt;=7.1.3

v3.2.1PHP &gt;=7.1.3|^8.0

### Community

Maintainers

![](https://www.gravatar.com/avatar/29566f243338aaa4a28dbf4cee26959331064aeab794fcb1472c18df170932d0?d=identicon)[tplaner](/maintainers/tplaner)

---

Top Contributors

[![tplaner](https://avatars.githubusercontent.com/u/50652?v=4)](https://github.com/tplaner "tplaner (94 commits)")[![akf](https://avatars.githubusercontent.com/u/35591?v=4)](https://github.com/akf "akf (20 commits)")[![Blindmikey](https://avatars.githubusercontent.com/u/115122?v=4)](https://github.com/Blindmikey "Blindmikey (4 commits)")[![jamespaulmuir](https://avatars.githubusercontent.com/u/79559?v=4)](https://github.com/jamespaulmuir "jamespaulmuir (3 commits)")[![boryn](https://avatars.githubusercontent.com/u/807297?v=4)](https://github.com/boryn "boryn (3 commits)")[![kvncrw](https://avatars.githubusercontent.com/u/3759919?v=4)](https://github.com/kvncrw "kvncrw (2 commits)")[![martinknor](https://avatars.githubusercontent.com/u/2004222?v=4)](https://github.com/martinknor "martinknor (2 commits)")[![eileenmcnaughton](https://avatars.githubusercontent.com/u/336308?v=4)](https://github.com/eileenmcnaughton "eileenmcnaughton (2 commits)")[![pospi](https://avatars.githubusercontent.com/u/437540?v=4)](https://github.com/pospi "pospi (2 commits)")[![trentonTrama](https://avatars.githubusercontent.com/u/41126812?v=4)](https://github.com/trentonTrama "trentonTrama (1 commits)")[![UltraSimplified](https://avatars.githubusercontent.com/u/522171?v=4)](https://github.com/UltraSimplified "UltraSimplified (1 commits)")[![BarryDam](https://avatars.githubusercontent.com/u/4747818?v=4)](https://github.com/BarryDam "BarryDam (1 commits)")[![deldreth](https://avatars.githubusercontent.com/u/233009?v=4)](https://github.com/deldreth "deldreth (1 commits)")[![DonaldGoose](https://avatars.githubusercontent.com/u/401283?v=4)](https://github.com/DonaldGoose "DonaldGoose (1 commits)")[![hikari-no-yume](https://avatars.githubusercontent.com/u/579774?v=4)](https://github.com/hikari-no-yume "hikari-no-yume (1 commits)")[![huebs](https://avatars.githubusercontent.com/u/1166532?v=4)](https://github.com/huebs "huebs (1 commits)")[![jacktuck](https://avatars.githubusercontent.com/u/8209433?v=4)](https://github.com/jacktuck "jacktuck (1 commits)")[![justinethier](https://avatars.githubusercontent.com/u/153000?v=4)](https://github.com/justinethier "justinethier (1 commits)")[![remicollet](https://avatars.githubusercontent.com/u/270445?v=4)](https://github.com/remicollet "remicollet (1 commits)")

---

Tags

calendardatephprruledatetimetimedaterecurrencerepeat

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/tplaner-when/health.svg)

```
[![Health](https://phpackages.com/badges/tplaner-when/health.svg)](https://phpackages.com/packages/tplaner-when)
```

###  Alternatives

[league/period

Time range API for PHP

7335.7M22](/packages/league-period)[brick/date-time

Date and time library

3663.6M93](/packages/brick-date-time)[aeon-php/calendar

PHP type safe, immutable calendar library

20410.2M16](/packages/aeon-php-calendar)[kartik-v/php-date-formatter

A Javascript datetime formatting and manipulation library using PHP date-time formats.

461.6M3](/packages/kartik-v-php-date-formatter)[dater/dater

Compact PHP library for working with date/time in different formats &amp; timezones.

14283.6k](/packages/dater-dater)[danielstjules/php-pretty-datetime

Generates human-readable strings for PHP DateTime objects

5797.0k](/packages/danielstjules-php-pretty-datetime)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
