Period

The Period value object

A Period instance is a PHP implementation of a bounded datetime interval which consists of:

infinite or unbounded intervals are not supported.

Instantiation

Instantiating a Period object is done using several named constructors describe below as the default constructor is made private. Whichever instance used, a Period instance is always created with the following requirements:

Both $startDate and $endDate parameters are datepoints. $endDate must be greater or equal to $startDate or the instantiation will throw a Period\IntervalError.

The $bounds is a League\Period\Bounds and only its value are eligible to create a new Period instance.

By default for each named constructor the $bounds is Bounds::IncludeStartExcludeEnd when not explicitly provided.

Using datepoints

public static Period::fromDate(
    DatePoint|DateTimeInterface|string $startDate, 
    DatePoint|DateTimeInterface|string $endDate, 
    Bounds $bounds = Bounds::IncludeStartExcludeEnd
): Period

If the timezone is important use a DateTimeInterface object instead of a string. When a string is provided, the timezone information is derived from the underlying system.

$day = Period::fromDate('2012-01-03', Datepoint::fromDateString('2012-02-03'), Bounds::ExcludeAll);
$day->toIso80000('Y-m-d'); //return (2012-01-03, 2012-02-03)

Using timestamp

public static Period::fromTimestamp(
    int $startDate, 
    int $endDate, 
    Bounds $bounds = Bounds::IncludeStartExcludeEnd
): Period
$day = Period::fromTimestamp(10125487, 10158962, Bounds::ExcludeAll);
$day->toIso80000('Y-m-d H:i:s'); //return (1970-04-28 04:0:07, 1970-04-28 13:0:02)

Using a datepoint and a duration

public static Period::after(
    DatePoint|DateTimeInterface|string $startDate, 
    Period|Duration|DateInterval|string $duration, 
    Bounds $bounds = Bounds::IncludeStartExcludeEnd
): Period
public static Period::before(
    DatePoint|DateTimeInterface|string $endDate,
    Period|Duration|DateInterval|string $duration,
    Bounds $bounds = Bounds::IncludeStartExcludeEnd
): Period
public static Period::around(
    DatePoint|DateTimeInterface|string $midpoint,
    Period|Duration|DateInterval|string $duration, 
    Bounds $bounds = Bounds::IncludeStartExcludeEnd
): Period

Examples

Using Period::after, Period::around, Period::before:

$date = '2012-04-01 08:30:25';
$duration = '1 DAY';
$half_duration = '12 HOURS';

$intervalAfter = Period::after($date, $duration);
$intervalBefore = Period::before($date->add($duration), $duration);
$intervalAfter->equals($intervalBefore); //returns true
$intervalAround = Period::around($date->add($half_duration), $half_duration);
$intervalAround->equals($intervalBefore); //returns true

Using date fields

The week index follows the ISO week date system. This means that the first week may be included in the previous year, conversely the last week may be included in the next year.

public static Period::fromDay(int $year, int $month, int $day, Bounds $bounds = Bounds::IncludeStartExcludeEnd): Period
public static Period::fromIsoWeek(int $year, int $week, Bounds $bounds = Bounds::IncludeStartExcludeEnd): Period
public static Period::fromMonth(int $year, int $month, Bounds $bounds = Bounds::IncludeStartExcludeEnd): Period
public static Period::fromQuarter(int $year, int $quarter, Bounds $bounds = Bounds::IncludeStartExcludeEnd): Period
public static Period::fromSemester(int $year, int $semester, Bounds $bounds = Bounds::IncludeStartExcludeEnd): Period
public static Period::fromYear(int $year, Bounds $bounds = Bounds::IncludeStartExcludeEnd): Period
public static Period::fromIsoYear(int $year, Bounds $bounds = Bounds::IncludeStartExcludeEnd): Period

The datepoints will be created following PHP DateTimeImmutable::setDate, DateTimeImmutable::setISODate and DateTimeImmutable::setTime rules
which means that overflow is possible and acceptable.

Examples

$day = Period::fromDay(2012, 1, 3);
$daybis = Period::fromDate('2012-01-03', '2012-01-04');
$day->equals($daybis); //return true;
$day->startDate->format('Y-m-d H:i:s'); //return 2012-01-03 00:00:00
$day->endDate->format('Y-m-d H:i:s'); //return 2012-01-04 00:00:00

Using standardized notation

public static Period::fromIso8601(string $format, string $notation, Bounds $bounds = Bounds::IncludeStartExcludeEnd): Period
public static Period::fromIso80000(string $format, string $notation): Period
public static Period::fromBourbaki(string $format, string $notation): Period

For better understanding:

Using ISO 8601 notation

The $notation should follow the {startDate}/{endDate} pattern where / serves as delimiter. Each endpoint should be formatted following the $format input;

$day = Period::fromIso8601('Y-m-d', '2012-01-03/2012-02-03');
echo $day->toIso80000('Y-m-d H:i:s'), //return [2012-01-03 21:38:22, 2012-02-03 21:38:22)

New in version 5.1 extended support for ISO8601 notation is added.

The previous example can be rewritten as follow:

$day = Period::fromIso8601('Y-m-d', '2012-01-03/02-03'); // the end value is extended (the year is skipped)
echo $day->toIso80000('Y-m-d H:i:s'), //return [2012-01-03 21:38:22, 2012-02-03 21:38:22)

or

$day = Period::fromIso8601('Y-m-d', '2012-01-03/P1M'); // the end value is a duration
echo $day->toIso80000('Y-m-d H:i:s'), //return [2012-01-03 21:38:22, 2012-02-03 21:38:22)

or

$day = Period::fromIso8601('Y-m-d', 'P1M/2012-01-03'); // the start value is a duration
echo $day->toIso80000('Y-m-d H:i:s'), //return [2012-01-03 21:38:22, 2012-02-03 21:38:22)

Using ISO 80000 notation

The $notation should follow the {lowerbound}{startDate},{endDate}{upperbound} where , serves as delimiter. Each endpoint should be formatted following the $format input. The possible bound values are:

$day = Period::fromIso80000('!Y-m-d', '[ 2012-01-03  , 2012-02-03 ]');
echo $day->toIso80000('Y-m-d H:i:s'); // returns [2012-01-03 00:00:00, 2012-02-03 00:00:00]
$day->bounds() === Bounds::IncludeAll;

Using Bourbaki notation

The $notation should follow the {lowerbound}{startDate},{endDate}{upperbound} where , serves as delimiter. Each endpoint should be formatted following the $format input. The possible bound values are:

$day = Period::fromBourbaki('!Y-m-d', '[ 2012-01-03  , 2012-02-03 [');
echo $day->toBourbaki('Y-m-d H:i:s'); // returns [2012-01-03 00:00:00, 2012-02-03 00:00:00[
$day->bounds() === Bounds::IncludeStartExcludeEnd;

Using a DatePeriod object

On PHP8.1

function Period::fromDateRange(
    DatePeriod $datePeriod,
    Bounds $bounds = Bounds::IncludeStartExcludeEnd
): self

Example

$daterange = new DatePeriod(
    new DateTime('2012-08-01'),
    new DateInterval('PT1H'),
    new DateTime('2012-08-31')
);
$interval = Period::fromDateRange($daterange);
$interval->startDate == $daterange->getStartDate();
$interval->endDate == $daterange->getEndDate();

If the submitted DatePeriod instance does not have a ending datepoint, It will trigger a TypeError error. This is possible if the DatePeriod instance was created using recurrences only

$dateRange = new DatePeriod('R4/2012-07-01T00:00:00Z/P7D');
$interval = Period::fromDateRange($dateRange);
//throws a TypeError error because $dateRange->getEndDate() returns null

On PHP8.1+

In PHP8.2+ the DatePeriod exposes full bound information that allow the removal of the extra optional parameter associated with Period::fromDateRange.

function Period::fromRange(DatePeriod $datePeriod): self

Example

use League\Period\Bounds;
use League\Period\Period;

$daterange = new DatePeriod(
    new DateTime('2012-08-01'),
    new DateInterval('PT1H'),
    new DateTime('2012-08-31'),
    DatePeriod::EXCLUDE_START_DATE | DatePeriod::INCLUDE_END_DATE
);
$interval = Period::fromRange($daterange);
$interval->startDate == $daterange->getStartDate();
$interval->endDate == $daterange->getEndDate();
$interval->bounds === Bounds::ExcludeStartIncludeEnd;

If the submitted DatePeriod instance does not have a ending datepoint, It will trigger a TypeError error. This is possible if the DatePeriod instance was created using recurrences only

$dateRange = new DatePeriod('R4/2012-07-01T00:00:00Z/P7D');
$interval = Period::fromRange($dateRange);
//throws a TypeError error because $dateRange->getEndDate() returns null

Since version 5.2.1 this method is made available in PHP8.1 but is restricted by DatePeriod features available.