PHPackages                             ckuran/period - 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. ckuran/period

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

ckuran/period
=============

Complex period comparisons

1.4.4(6y ago)028MITPHPPHP ^7.1

Since Nov 27Pushed 5y agoCompare

[ Source](https://github.com/ckuran/period)[ Packagist](https://packagist.org/packages/ckuran/period)[ Docs](https://github.com/spatie/period)[ RSS](/packages/ckuran-period/feed)WikiDiscussions master Synced 5d ago

READMEChangelogDependencies (3)Versions (27)Used By (0)

Complex period comparisons
==========================

[](#complex-period-comparisons)

[![Latest Version on Packagist](https://camo.githubusercontent.com/978fea0fb023ef1f977984e4a99fd3c6a3dd0a5d6e14ecf336bfc2c7e375f45a/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f7370617469652f706572696f642e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/spatie/period)[![Build Status](https://camo.githubusercontent.com/93f56abb99ee08e79a43bb1a445fafcb99ad83641cf6bee4fd7711a101171899/68747470733a2f2f696d672e736869656c64732e696f2f7472617669732f7370617469652f706572696f642f6d61737465722e7376673f7374796c653d666c61742d737175617265)](https://travis-ci.org/spatie/period)[![Quality Score](https://camo.githubusercontent.com/c1e8824f79147554f98fdc6b3ee228f22c8cdf6370b9bedf6c0cf88eb2c0be0f/68747470733a2f2f696d672e736869656c64732e696f2f7363727574696e697a65722f672f7370617469652f706572696f642e7376673f7374796c653d666c61742d737175617265)](https://scrutinizer-ci.com/g/spatie/period)[![StyleCI](https://camo.githubusercontent.com/5bc44cb98a93f7a88987883c0561988d75a936affa2fae6d1a3a2599ba98f28d/68747470733a2f2f6769746875622e7374796c6563692e696f2f7265706f732f3135393135353836332f736869656c643f6272616e63683d6d6173746572)](https://github.styleci.io/repos/159155863)[![Total Downloads](https://camo.githubusercontent.com/95e86285f784024af273a2813b8516ffb0710b7e706b24dc9929c341fd52377d/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f7370617469652f706572696f642e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/spatie/period)

This package adds support for comparing multiple dates with each other. You can calculate the overlaps and differences between n-amount of periods, as well as some more basic comparisons between two periods.

Periods can be constructed from any type of `DateTime` implementation, making this package compatible with custom `DateTime` implementations like [Carbon](https://carbon.nesbot.com)(see [cmixin/enhanced-period](https://github.com/kylekatarnls/enhanced-period) to convert directly from and to CarbonPeriod).

Periods are always immutable, there's never the worry about your input dates being changed.

This package is still a work in progress.

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

[](#installation)

You can install the package via composer:

```
composer require spatie/period
```

Usage
-----

[](#usage)

### Quick reference

[](#quick-reference)

#### Creating a period

[](#creating-a-period)

```
$period = new Period(
     DateTimeImmutable $start
   , DateTimeImmutable $end
  [, ?int $precisionMask = Precision::DAY]
  [, ?int $boundaryExclusionMask = Boundaries::EXCLUDE_NONE]
)
```

The static `::make` constructor can also take strings and other implementations of `DateTimeInterface`, as well as an extra format string, in case the textual dates passed aren't of the format `Y-m-d` or `Y-m-d H:i:s`.

```
$period = Period::make(
     string|DateTimeInterface $start
   , string|DateTimeInterface $end
  [, ?int Precision::DAY]
  [, ?int Boundaries::EXCLUDE_NONE]
  [, string $format]
)
```

#### Length and boundaries

[](#length-and-boundaries)

```
$period->length(): int
```

```
$period->startIncluded(): bool
$period->startExcluded(): bool
$period->endIncluded(): bool
$period->endExcluded(): bool
```

```
$period->getStart(): DateTimeImmutable
$period->getStartIncluded(): DateTimeImmutable
$period->getEnd(): DateTimeImmutable
$period->getEndIncluded(): DateTimeImmutable
```

#### Comparisons

[](#comparisons)

```
$period->contains(DateTimeInterface $date): bool
$period->equals(Period $period): bool

$period->overlapsWith(Period $period): bool
$period->touchesWith(Period $period): bool
```

```
$period->startsAt(DateTimeInterface $date): bool
$period->startsBefore(DateTimeInterface $date): bool
$period->startsBeforeOrAt(DateTimeInterface $date): bool
$period->startsAfter(DateTimeInterface $date): bool
$period->startsAfterOrAt(DateTimeInterface $date): bool
```

```
$period->endsAt(DateTimeInterface $date): bool
$period->endsBefore(DateTimeInterface $date): bool
$period->endsBeforeOrAt(DateTimeInterface $date): bool
$period->endsAfter(DateTimeInterface $date): bool
$period->endsAfterOrAt(DateTimeInterface $date): bool
```

```
$period->gap(Period $period): ?Period
```

```
$period->overlapSingle(Period $period): ?Period
$period->overlap(Period ...$periods): PeriodCollection
$period->overlapAll(Period ...$periods): Period
```

```
$period->diffSingle(Period $period): PeriodCollection
$period->diff(Period ...$periods): PeriodCollection
```

```
$periodCollection->overlap(PeriodCollection ...$periodCollections): PeriodCollection
$periodCollection->overlapSingle(PeriodCollection $periodCollection): PeriodCollection
$periodCollection->map(Closure $closure): PeriodCollection
$periodCollection->reduce(Closure $closure): mixed
```

```
$periodCollection->boundaries(): ?Period
```

```
$periodCollection->gaps(): PeriodCollection
```

### Comparing periods

[](#comparing-periods)

**Overlaps with any other period**: this method returns a `PeriodCollection` multiple `Period` objects representing the overlaps.

```
/*
 * A       [========]
 * B                    [==]
 * C                            [=====]
 * CURRENT        [===============]
 *
 * OVERLAP        [=]   [==]    [=]
 */

$a = Period::make('2018-01-01', '2018-01-31');
$b = Period::make('2018-02-10', '2018-02-20');
$c = Period::make('2018-03-01', '2018-03-31');

$current = Period::make('2018-01-20', '2018-03-10');

$overlaps = $current->overlap($a, $b, $c);
```

**Overlap with all periods**: this method only returns one period where all periods overlap.

```
/*
 * A              [============]
 * B                   [==]
 * C                  [=======]
 *
 * OVERLAP             [==]
 */

$a = Period::make('2018-01-01', '2018-01-31');
$b = Period::make('2018-01-10', '2018-01-15');
$c = Period::make('2018-01-10', '2018-01-31');

$overlap = $a->overlapAll($b, $c);
```

**Diff between multiple periods**: this method returns a `PeriodCollection` multiple `Period` objects representing the diffs between several periods and one.

```
/*
 * A                   [====]
 * B                               [========]
 * C         [=====]
 * CURRENT      [========================]
 *
 * DIFF             [=]      [====]
 */

$a = Period::make('2018-01-05', '2018-01-10');
$b = Period::make('2018-01-15', '2018-03-01');
$c = Period::make('2017-01-01', '2018-01-02');

$current = Period::make('2018-01-01', '2018-01-31');

$diff = $current->diff($a, $b, $c);
```

**Overlaps with**: this method returns a boolean indicating of two periods overlap or not.

```
/*
 * A              [============]
 * B                   [===========]
 */

$a = Period::make('2018-01-01', '2018-01-31');
$b = Period::make('2018-01-10', '2018-02-15');

$overlap = $a->overlapsWith($b); // true
```

**Touches with**: this method determines if two periods touch each other.

```
/*
 * A              [========]
 * B                        [===========]
 */

$a = Period::make('2018-01-01', '2018-01-31');
$b = Period::make('2018-02-01', '2018-02-15');

$overlap = $a->touchesWith($b); // true
```

**Gap**: returns the gap between two periods. If no gap exists, `null` is returned.

```
/*
 * A              [========]
 * B                           [===========]
 */

$a = Period::make('2018-01-01', '2018-01-31');
$b = Period::make('2018-02-05', '2018-02-15');

$overlap = $a->gap($b); // Period('2018-02-01', '2018-02-04')
```

**Boundaries of a collection**: get one period representing the boundaries of a collection.

```
/*
 * A                   [====]
 * B                               [========]
 * C           [=====]
 * D                                             [====]
 *
 * BOUNDARIES  [======================================]
 */

$collection = new PeriodCollection(
    Period::make('2018-01-01', '2018-01-05'),
    Period::make('2018-01-10', '2018-01-15'),
    Period::make('2018-01-20', '2018-01-25'),
    Period::make('2018-01-30', '2018-01-31')
);

$boundaries = $collection->boundaries();
```

**Gaps of a collection**: get all the gaps of a collection.

```
/*
 * A                   [====]
 * B                               [========]
 * C         [=====]
 * D                                             [====]
 *
 * GAPS             [=]      [====]          [==]
 */

$collection = new PeriodCollection(
    Period::make('2018-01-01', '2018-01-05'),
    Period::make('2018-01-10', '2018-01-15'),
    Period::make('2018-01-20', '2018-01-25'),
    Period::make('2018-01-30', '2018-01-31')
);

$gaps = $collection->gaps();
```

**Overlap multiple collections**: returns the overlap between collections. This means an AND operation between collections, and an OR operation within the same collection.

```
/*
 * A            [=====]      [===========]
 * B            [=================]
 * C                [====================]
 *
 * OVERLAP          [=]      [====]
 */

$a = new PeriodCollection(
    Period::make('2018-01-01', '2018-01-07'),
    Period::make('2018-01-15', '2018-01-25')
);

$b = new PeriodCollection(
    Period::make('2018-01-01', '2018-01-20')
);

$c = new PeriodCollection(
    Period::make('2018-01-06', '2018-01-25')
);

$overlap = $a->overlap($b, $c);
```

### Working with `PeriodCollection`

[](#working-with-periodcollection)

Period collections are constructed from several periods:

```
$collection = new PeriodCollection(
    Period::make('2018-01-01', '2018-01-02'),
    // …
);
```

They may be looped over directly and its contents will be recognised by your IDE:

```
$collection = new PeriodCollection(/* … */);

foreach ($collection as $period) {
    $period->…
}
```

You may destruct them:

```
[$firstPeriod, $secondPeriod, $thirdPeriod] = $collection;
```

And finally construct one collection from another:

```
$newCollection = new PeriodCollection(...$otherCollection);
```

### Precision

[](#precision)

Date precision is of utmost importance if you want to reliably compare two periods. The the following example:

> Given two periods: `[2018-01-01, 2018-01-15]` and `[2018-01-15, 2018-01-31]`; do they overlap?

At first glance the answer is "yes": they overlap on `2018-01-15`. But what if the first period ends at `2018-01-15 10:00:00`, while the second starts at `2018-01-15 15:00:00`? Now they don't anymore!

This is why this package requires you to specify a precision with each period. Only periods with the same precision can be compared.

A more in-depth explanation on why precision is so important can be found [here](https://stitcher.io/blog/comparing-dates). A period's precision can be specified when constructing that period:

```
Period::make('2018-01-01', '2018-02-01', Precision::DAY);
```

The default precision is set on days. These are the available precision options:

```
Precision::YEAR
Precision::MONTH
Precision::DAY
Precision::HOUR
Precision::MINUTE
Precision::SECOND
```

### Boundaries

[](#boundaries)

By default, period comparisons are done with included boundaries. This means that these two periods overlap:

```
$a = Period::make('2018-01-01', '2018-02-01');
$b = Period::make('2018-02-01', '2018-02-28');

$a->overlapsWith($b); // true
```

The length of a period will also include both boundaries:

```
$a = Period::make('2018-01-01', '2018-01-31');

$a->length(); // 31
```

It's possible to override the boundary behaviour:

```
$a = Period::make('2018-01-01', '2018-02-01', null, Boundaries::EXCLUDE_END);
$b = Period::make('2018-02-01', '2018-02-28', null, Boundaries::EXCLUDE_END);

$a->overlapsWith($b); // false
```

There are four types of boundary exclusion:

```
Boundaries::EXCLUDE_NONE;
Boundaries::EXCLUDE_START;
Boundaries::EXCLUDE_END;
Boundaries::EXCLUDE_ALL;
```

### Compatibility

[](#compatibility)

You can construct a `Period` from any type of `DateTime` object such as Carbon:

```
Period::make(Carbon::make('2018-01-01'), Carbon::make('2018-01-02'));
```

Note that as soon as a period is constructed, all further operations on it are immutable. There's never the danger of changing the input dates.

You can iterate a `Period` like a regular `DatePeriod` with the precision specified on creation:

```
$datePeriod = Period::make(Carbon::make('2018-01-01'), Carbon::make('2018-01-31'));

foreach ($datePeriod as $date) {
    /** @var DateTimeImmutable $date */
    // 2018-01-01
    // 2018-01-02
    // ...
    // (31 iterations)
}

$timePeriod = Period::make(Carbon::make('2018-01-01 00:00:00'), Carbon::make('2018-01-01 23:59:59'), Precision::HOUR);

foreach ($timePeriod as $time) {
    /** @var DateTimeImmutable $time */
    // 2018-01-01 00:00:00
    // 2018-01-01 01:00:00
    // ...
    // (24 iterations)
}
```

### Visualizing periods

[](#visualizing-periods)

You can visualize one or more `Period` objects as well as `PeriodCollection`objects to see how they related to one another:

```
$visualizer = new Visualizer(["width" => 27]);

$visualizer->visualize([
    "A" => Period::make('2018-01-01', '2018-01-31'),
    "B" => Period::make('2018-02-10', '2018-02-20'),
    "C" => Period::make('2018-03-01', '2018-03-31'),
    "D" => Period::make('2018-01-20', '2018-03-10'),
    "OVERLAP" => new PeriodCollection(
        Period::make('2018-01-20', '2018-01-31'),
        Period::make('2018-02-10', '2018-02-20'),
        Period::make('2018-03-01', '2018-03-10')
    ),
]);
```

And visualize will return the following string:

```
A          [========]
B                      [==]
C                           [========]
D               [==============]
OVERLAP         [===]  [==] [==]

```

The visualizer has a configurable width provided upon creation which will control the bounds of the displayed periods:

```
$visualizer = new Visualizer(["width" => 10]);
```

### Testing

[](#testing)

```
composer test
```

### Changelog

[](#changelog)

Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.

Contributing
------------

[](#contributing)

Please see [CONTRIBUTING](CONTRIBUTING.md) for details.

### Security

[](#security)

If you discover any security related issues, please email  instead of using the issue tracker.

Postcardware
------------

[](#postcardware)

You're free to use this package, but if it makes it to your production environment we highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using.

Our address is: Spatie, Samberstraat 69D, 2060 Antwerp, Belgium.

We publish all received postcards [on our company website](https://spatie.be/en/opensource/postcards).

Credits
-------

[](#credits)

- [Brent Roose](https://github.com/brendt)
- [All Contributors](../../contributors)

Support us
----------

[](#support-us)

Spatie is a webdesign agency based in Antwerp, Belgium. You'll find an overview of all our open source projects [on our website](https://spatie.be/opensource).

Does your business depend on our contributions? Reach out and support us on [Patreon](https://www.patreon.com/spatie). All pledges will be dedicated to allocating workforce on maintenance and new awesome stuff.

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

###  Health Score

29

—

LowBetter than 59% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity7

Limited adoption so far

Community14

Small or concentrated contributor base

Maturity66

Established project with proven stability

 Bus Factor1

Top contributor holds 72.9% 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 ~10 days

Recently: every ~25 days

Total

24

Last Release

2476d ago

Major Versions

0.5.1 → 1.0.02019-01-17

1.4.4 → 2.0.x-dev2019-08-05

PHP version history (2 changes)0.1.0PHP ^7.1

2.0.x-devPHP ^7.3

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/29108338?v=4)[Chris K.](/maintainers/ckuran)[@ckuran](https://github.com/ckuran)

---

Top Contributors

[![brendt](https://avatars.githubusercontent.com/u/6905297?v=4)](https://github.com/brendt "brendt (97 commits)")[![freekmurze](https://avatars.githubusercontent.com/u/483853?v=4)](https://github.com/freekmurze "freekmurze (19 commits)")[![jeromegamez](https://avatars.githubusercontent.com/u/67554?v=4)](https://github.com/jeromegamez "jeromegamez (4 commits)")[![winkbrace](https://avatars.githubusercontent.com/u/1883930?v=4)](https://github.com/winkbrace "winkbrace (3 commits)")[![ckuran](https://avatars.githubusercontent.com/u/29108338?v=4)](https://github.com/ckuran "ckuran (3 commits)")[![kylekatarnls](https://avatars.githubusercontent.com/u/5966783?v=4)](https://github.com/kylekatarnls "kylekatarnls (2 commits)")[![thecrypticace](https://avatars.githubusercontent.com/u/614993?v=4)](https://github.com/thecrypticace "thecrypticace (2 commits)")[![svenluijten](https://avatars.githubusercontent.com/u/11269635?v=4)](https://github.com/svenluijten "svenluijten (1 commits)")[![andreybolonin](https://avatars.githubusercontent.com/u/2576509?v=4)](https://github.com/andreybolonin "andreybolonin (1 commits)")[![savamarkovic](https://avatars.githubusercontent.com/u/1781177?v=4)](https://github.com/savamarkovic "savamarkovic (1 commits)")

---

Tags

spatieperiod

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/ckuran-period/health.svg)

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

###  Alternatives

[spatie/period

Complex period comparisons

1.7k4.0M10](/packages/spatie-period)[spatie/laravel-package-tools

Tools for creating Laravel packages

935125.5M7.0k](/packages/spatie-laravel-package-tools)[spatie/laravel-data

Create unified resources and data transfer objects

1.7k28.9M627](/packages/spatie-laravel-data)[spatie/laravel-analytics

A Laravel package to retrieve Google Analytics data.

3.2k5.7M57](/packages/spatie-laravel-analytics)[spatie/macroable

A trait to dynamically add methods to a class

72659.6M64](/packages/spatie-macroable)[spatie/regex

A sane interface for php's built in preg\_\* functions

1.1k17.1M59](/packages/spatie-regex)

PHPackages © 2026

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