PHPackages                             moves/fowler-recurring-events - 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. moves/fowler-recurring-events

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

moves/fowler-recurring-events
=============================

3.0.0(3y ago)25.8k[1 issues](https://github.com/vetmoves/com.moves.php.fowler-recurring-events/issues)1MITPHPPHP ^8.1

Since Sep 8Pushed 2y ago2 watchersCompare

[ Source](https://github.com/vetmoves/com.moves.php.fowler-recurring-events)[ Packagist](https://packagist.org/packages/moves/fowler-recurring-events)[ RSS](/packages/moves-fowler-recurring-events/feed)WikiDiscussions 1.x Synced 3w ago

READMEChangelogDependencies (7)Versions (44)Used By (1)

FowlerRecurringEvents
=====================

[](#fowlerrecurringevents)

Introduction
------------

[](#introduction)

This library is an implementation of Martin Fowler's algorithms for recurring calendar events. See  for more information.

This library is divided into several "Temporal Expression" classes, each of which can be used to determine whether an event occurs on a specific target date according to the specified pattern.

There are several different types of patterns included here which to the best of our knowledge are the most common amongst mainstream calendar providers such as Google Calendar.

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

[](#installation)

To add this library into your project, run:

```
composer require moves/fowler-recurring-events

```

Available Patterns
------------------

[](#available-patterns)

Note that all patterns also include a `frequency` option which allows you to specify how often the pattern applies. For example, for the **Days of Week** pattern, you might want to specify recurrence every *other* weeks, rather than every week. In this case, you would set a `frequency` of 2 to specify that the pattern applies every 2 weeks.

- **Day of Month** - Recurrence on a specific numbered day of every month
    - 1 = 1st day of every month
    - 10 = 10th day of every month
    - -1 = Last day of every month
    - -2 = 2nd to last day of every month
- **Day of Week of Month** - Recurrence on a specific weekday on a numbered week of every month
    - (3, 1) = 1st Wednesday of every month
    - (1, 2) = 2nd Monday of every month
    - (6, -1) = Last Saturday of every month
    - (7, -2) = 2nd to last Sunday of every month
- **Day of Year** - Recurrence on a specific calendar date every year
    - (25, 12) = December 25th of every year
    - (1, 4) = April 1st of every year
    - (29, 2) = February 29th on every leap year, March 1 on every non-leap year
- **Days** - Recurrence repeating on a certain number of days
    - 1 = Every day
    - 2 = Every other day
    - 10 = Every 10 days
- **Days of Week** - Recurrence on certain days of the week
    - \[1, 2, 3, 4, 5\] = Every weekday (Monday - Friday)
    - \[6, 7\] = Every weekend (Saturday - Sunday)
    - \[1, 5\] = Every Monday and Friday

Usage
-----

[](#usage)

To determine whether your repeating event occurs on a particular target date, first instantiate an instance of the appropriate Temporal Expression class with the details of your recurrence pattern, then call the `includes()` function.

Also note that all temporal expressions require a date on which the pattern begins.

For example, for an event which repeats every December 25th:

```
use Moves\FowlerRecurringEvents\TemporalExpressions\TEDayOfYear;

$patternStart = new DateTime('336-12-25'); //First recorded Christmas celebration
$eventPattern = new TEDayOfYear($patternStart, 25, 12);

$targetDate = new DateTime('2021-12-25');

$eventOccursOnTargetDate = $eventPattern->includes($targetDate);
// Expected Result: true

$otherTargetDate = new DateTime('2022-01-01');

$eventOccursOnOtherTargetDate = $eventPattern->includes($otherTargetDate);
// Expected Result: false

```

### Frequency and Pattern End Date

[](#frequency-and-pattern-end-date)

The frequency and pattern end date properties are optional when constructing a temporal expression. To set values, for these optional properties, use the setter functions.

```
$eventPattern->setFrequency(2);
$eventPattern->setEndDate(new DateTime('2022-01-01'));

```

### Builder Pattern

[](#builder-pattern)

You can use the builder pattern with setter method chaining for more convenience when building your temporal expressions.

```
use Moves\FowlerRecurringEvents\TemporalExpressions\TEDaysOfWeek;

$eventStart = new DateTime('2021-01-04');
$eventEnd = new DateTime('2021-12-27');
$eventPattern = TEDaysOfWeek::build($eventStart, 1)
  ->setEndDate($eventEnd)
  ->setFrequency(2);

```

Practical Usage and Advice
--------------------------

[](#practical-usage-and-advice)

The "Temporal Expression" algorithms are not intended to exist in a vacuum. In almost every instance, you will want to store a single "master" instance for the recurrence pattern, usually the first occurrence, along with the type of recurrence pattern or Temporal Expression and the required details of that particular pattern.

For developers who are inexperienced with calendar systems, it can be incredibly tempting to simply pre-calculate and store every possible instance of a repeating calendar event. However, doing so comes with massive inefficiencies as the runtime *and* data storage complexity for creating, editing, and deleting a series of events grows linearly the further out you intend to project. Plus, using this method, your projection has to stop somewhere, otherwise you would be stuck creating instances off into infinity, which in turn means that you must impose an end date to the pattern on your users.

Alternatively, you might think improve the data storage complexity and circumvent the "end date" limitation by storing a single "master" instance, and dynamically project that instance into the future. While this is the correct thought process, it is another place where inexperienced developers often run astray. The temptation is often to simply iterate over your pattern, day by day, week by week, month by month, or year by year, from the start date to the target projection date. With this method, of course, the runtime grows linearly the further out you intend to project, and thus it is not ideal for production calendar systems.

The Fowler methodology describes `O(1)` time algorithms for projecting calendar events, meaning that no matter how far out you intend to project, the runtime is always the same. If you intend to project out a list of calendar events that will occur during a specified period of time, such as rendering a month at a time in a Calendar GUI, you can query a list of "master" events, then iterate over each day you want to create a projection for, checking the Temporal expression for each master event on each day to dynamically build your list.

There are, of course, many many optimizations that you can apply to reduce the number of cases you have to check and calculate for when projecting a list of events for a specific period of time, but those are outside of the scope of this library.

###  Health Score

32

—

LowBetter than 69% of packages

Maintenance10

Infrequent updates — may be unmaintained

Popularity21

Limited adoption so far

Community10

Small or concentrated contributor base

Maturity72

Established project with proven stability

 Bus Factor1

Top contributor holds 100% 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 ~20 days

Recently: every ~36 days

Total

43

Last Release

919d ago

Major Versions

0.0.0 → 1.0.0-rc.12021-10-20

1.3.10 → 2.0.02022-09-07

1.x-dev → 2.0.12022-12-07

2.x-dev → 3.0.02023-04-10

3.x-dev → 4.0.0-beta.12023-07-14

PHP version history (2 changes)0.0.0PHP ^7.3|^8.0

2.0.0PHP ^8.1

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/89610601?v=4)[moves-ext-devs](/maintainers/moves-ext-devs)[@moves-ext-devs](https://github.com/moves-ext-devs)

---

Top Contributors

[![clarkj99](https://avatars.githubusercontent.com/u/17483218?v=4)](https://github.com/clarkj99 "clarkj99 (3 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/moves-fowler-recurring-events/health.svg)

```
[![Health](https://phpackages.com/badges/moves-fowler-recurring-events/health.svg)](https://phpackages.com/packages/moves-fowler-recurring-events)
```

###  Alternatives

[illuminate/support

The Illuminate Support package.

582110.9M39.8k](/packages/illuminate-support)[craftcms/feed-me

Import content from XML, RSS, CSV or JSON feeds into entries, categories, Craft Commerce products, and more.

292943.4k27](/packages/craftcms-feed-me)[flarum/core

Delightfully simple forum software.

201.4M2.2k](/packages/flarum-core)[solspace/craft-freeform

The most flexible and user-friendly form building plugin!

53675.5k15](/packages/solspace-craft-freeform)[northwestern-sysdev/dynamic-forms

Dynamic Forms for Laravel

4957.8k2](/packages/northwestern-sysdev-dynamic-forms)[japanese-date/japanese-date

日本の暦、祝日を取り扱うライブラリ

169.9k](/packages/japanese-date-japanese-date)

PHPackages © 2026

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