spatie/period
Immutable date period objects for PHP with powerful comparison tools. Create periods from any DateTime (including Carbon), then calculate overlaps, gaps, intersections, differences, containment, and other complex comparisons across two or many periods.
Install the package with composer require spatie/period. Start by creating Period instances using Period::make($start, $end), which automatically sets precision to DAY and boundaries to include both ends. This is sufficient for most day-based use cases like overlapping booking periods or event schedules.
The first critical concept to grasp is precision—comparison operations only work between periods with identical precision. For example, a DAY-precision period won’t correctly overlap with an HOUR-precision one. Always match precision to your domain needs (e.g., use Precision::HOUR() for time-slot scheduling). Also note that while Period::make handles boundaries and precision for you, manual construction (new Period(...)) requires explicit Precision and Boundaries arguments.
Begin with basic comparisons:
$periodA = Period::make('2024-01-01', '2024-01-10');
$periodB = Period::make('2024-01-08', '2024-01-15');
$periodA->overlapsWith($periodB); // true
$periodA->contains('2024-01-05'); // true
Use PeriodCollection to manage groups of periods and perform set operations across them. Common workflows include:
Scheduling conflicts: Use overlapAny() to find all overlapping sub-periods across multiple events or resources.
$collection = new PeriodCollection($booking1, $booking2, $booking3);
$conflicts = $collection->overlapAny(); // Returns all pairwise overlaps
Availability calculation: Use subtract() to carve out booked or unavailable periods from a total window.
$availability = Period::make('2024-06-01', '2024-06-30');
$blocked = [Period::make('2024-06-05', '2024-06-08'), Period::make('2024-06-20', '2024-06-22')];
$freeSlots = $availability->subtract(...$blocked);
Merging contiguous periods: Use PeriodCollection::union() to consolidate overlapping or touching periods (e.g., consolidating overlapping project timelines).
$merged = $collection->union();
Finding gaps in sequences: PeriodCollection::gaps() returns periods between the inputs where no coverage exists—useful for identifying missed billing periods or maintenance gaps.
Leverage the Visualizer for debugging complex scenarios during development:
$visualizer = new Spatie\Period\Visualizer(['width' => 30]);
echo $visualizer->visualize([
'A' => $periodA,
'B' => $periodB,
'GAP' => $periodA->gap($periodB),
]);
Support for Carbon or native DateTime is seamless—construct periods from either, and all operations remain immutable and type-safe.
Precision must match exactly—Period::make('2024-01-01', '2024-01-02', Precision::HOUR()) vs Precision::DAY() will cause comparisons like overlapsWith() to throw an exception or behave unexpectedly. Always validate precision before comparing collections.
Boundaries affect inclusion semantics: By default, end dates are included, so Period::make('01-01', '01-10') spans 10 days (not 9). If using half-open intervals (e.g., EXCLUDE_END), adjust logic accordingly. E.g., Boundaries::EXCLUDE_END() makes [01-01, 01-10) 9 days, and periods like [01-10, 01-15) will not overlap.
length() returns integer units—e.g., 10 for 10 days. Use duration() for a PeriodDuration object with more detailed breakdowns if needed.
PeriodCollection is immutable: Methods like add(), filter(), sort(), and union() return new collections—don’t assume in-place mutation.
Visualizer width matters: A too-narrow Visualizer collapses overlapping periods, making gaps or overlaps appear falsely contiguous. Use a wider width or narrow your timespan for clarity.
Empty PeriodCollection handling: Subtracting an empty collection has no effect, but operations like gaps() on a single period return empty collections—always check isEmpty() before consuming.
No native recurrence support: This package only handles static intervals. Use Period::renew() for simple repeat patterns (e.g., next week, next month) but handle looping and daylight saving manually.
Threading safety: Since periods are immutable, they’re safe for concurrent or queue-based workflows—but ensure precise input values to avoid off-by-one errors across timezones (always convert to UTC before constructing).
How can I help you explore Laravel packages today?