Generating Recurring Dates with PHP DatePeriod

Managing recurring events like weekly newsletters, monthly billing cycles, or annual anniversaries is a common task in web development. This article provides a straightforward guide on how to use PHP’s built-in DatePeriod class to easily generate a series of recurring dates. You will learn how to configure the class using DateTime and DateInterval objects, control the number of recurrences, and exclude start dates through practical, ready-to-use code examples.

Understanding the DatePeriod Class

The DatePeriod class allows you to traverse through a set of dates that recur at regular intervals over a given period. To create a DatePeriod instance, you need three main components:

  1. Start Date: A DateTime or DateTimeImmutable object marking the beginning of the period.
  2. Interval: A DateInterval object representing the duration between each recurrence (e.g., daily, weekly, monthly).
  3. End Point: This can be either an end DateTime object or an integer specifying the number of recurrences.

Method 1: Generating Dates Using a Start and End Date

If you know the exact start and end dates of your schedule, you can pass both dates along with the interval to the DatePeriod constructor.

The following example generates a weekly schedule for the month of October:

<?php

$start = new DateTime('2024-10-01');
$interval = new DateInterval('P1W'); // Interval of 1 week (P = Period, 1W = 1 Week)
$end = new DateTime('2024-11-01');

$period = new DatePeriod($start, $interval, $end);

foreach ($period as $date) {
    echo $date->format('Y-m-d') . "\n";
}

Output:

2024-10-01
2024-10-08
2024-10-15
2024-10-22
2024-10-29

Note: The end date is exclusive by default. If the last recurrence falls exactly on the end date, it will not be included in the output.


Method 2: Generating Dates Using a Number of Recurrences

If you want to generate a specific number of recurring events rather than defining an end date, you can pass an integer as the third argument instead of an end DateTime object.

The following example generates the next 5 monthly billing dates starting from October 1st:

<?php

$start = new DateTime('2024-10-01');
$interval = new DateInterval('P1M'); // Interval of 1 month (P = Period, 1M = 1 Month)
$recurrences = 5; // Generate 5 additional occurrences

$period = new DatePeriod($start, $interval, $recurrences);

foreach ($period as $date) {
    echo $date->format('Y-m-d') . "\n";
}

Output:

2024-10-01
2024-11-01
2024-12-01
2025-01-01
2025-02-01
2025-03-01

Excluding the Start Date

By default, the initial start date is included as the first result in your loop. If you want the recurrences to begin strictly after the start date, you can pass the flag DatePeriod::EXCLUDE_START_DATE as the fourth parameter to the constructor.

<?php

$start = new DateTime('2024-10-01');
$interval = new DateInterval('P2D'); // Every 2 days
$end = new DateTime('2024-10-10');

// Pass the exclusion flag as the fourth argument
$period = new DatePeriod($start, $interval, $end, DatePeriod::EXCLUDE_START_DATE);

foreach ($period as $date) {
    echo $date->format('Y-m-d') . "\n";
}

Output:

2024-10-03
2024-10-05
2024-10-07
2024-10-09