spatie/opening-hours: PHP schedules, exceptions, and overflow
Query and format a set of opening hours
At a glance
- What is it?
- spatie/opening-hours represents recurring business hours, dated exceptions, and after-midnight ranges as a queryable PHP object. It fits applications that need schedule decisions and day-level formatting, but the README does not document installation commands, persistence, localization, or rollback.
- Who is it for?
- Adopt spatie/opening-hours when a PHP application needs a programmatic model for recurring weekly hours, holiday exceptions, and overnight ranges. Do not choose it as a complete scheduling platform without first confirming requirements it does not document, including persistence, localization, and operational rollback.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 3 days ago.
- What is it written in?
- Mainly PHP, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 7, 2026, and from our analysis. They are not legal advice.
Star history
- spatie/opening-hours1,721 ★ · +1
| Repository | 2026-10-16 | 2026-11-07 |
|---|---|---|
| spatie/opening-hours | 1.7k | 1.7k |
1 Stars in the last 22 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
The schedule model spatie/opening-hours solves
spatie/opening-hours addresses a recurring problem in PHP applications: representing when a business is open without scattering schedule checks across controllers, views, and domain code. It is intended for applications that need to query a weekly schedule, apply date-specific exceptions, and expose opening periods for display. The schedule is not stored as a collection of unrelated booleans. It is represented by an `OpeningHours` object built from day definitions and an `exceptions` list.
That model supports several distinct questions. An application can ask whether a weekday is generally open, whether a precise date and time falls inside an opening period, when the current period began, or when the business will next open. It can also return schedule objects for individual days, weeks, dates, or exceptions. This separation matters when the same rules drive both user-facing labels and application behavior.
The package is for PHP developers who want those calculations inside their own code. The README also documents integration with enhanced `Carbon` objects through `cmixin/business-time`. It does not claim to be a database, administrative calendar, booking service, or distributed scheduling service. Those boundaries make it more suitable as a schedule rule component than as a replacement for an operations platform.
Regular schedules and dated exception resolution
A regular schedule assigns time ranges to named weekdays. Each day can contain multiple periods, such as a morning interval and an afternoon interval, and an empty array represents a day without regular hours. The object can answer `isOpenOn('monday')` for the recurring schedule or `isOpenAt(new DateTime('2016-09-26 19:00:00'))` when a precise moment matters.
The `exceptions` array adds another resolution layer. A full date such as `2016-12-25` can replace the normal behavior for that specific date, while `01-01` can describe a recurring exception on every first day of January. An empty hours list is used to close a date or recurring holiday. A holiday schedule is therefore represented inside the schedule object rather than in separate application code that checks a date before consulting `OpeningHours`.
Day ranges reduce repetition for contiguous schedules. The README shows `monday to friday` and `saturday to sunday`, as well as dated spans such as `2024-06-25 to 2024-07-01`. Full dates and month-day forms therefore coexist in the same input model. The README does not fully document conflict behavior when multiple exceptions match one date, so an adoption should test that rule against the application's data before depending on it.
For display, `forDay()`, `forWeek()`, `forWeekCombined()`, `forDate()`, and `exceptions()` expose different views. The combined weekly method groups days that share a schedule, while date-based methods account for exceptions. These are separate operations, so choosing the wrong one could present regular Monday hours when Christmas exceptions should apply.
Overnight hours and the overflow switch
The `overflow` option addresses schedules that cross midnight, which is important for venues such as nightclubs. When enabled during construction, a period such as `20:00-03:00` can continue into the following day. According to the README, the API checks the previous day's data when evaluating the continuation of that time range.
The switch is opt-in. That is a useful boundary because cross-day interpretation is not merely a formatting detail. A schedule that says `friday` and `saturday` from `20:00-03:00` has different operational meaning from two independent early-morning periods. Enabling overflow makes that overnight relationship part of schedule evaluation.
The README also shows another midnight representation without relying on the same example: `22:00-24:00` on Wednesday followed by `00:00-07:00` on Thursday. This approach records the two calendar-day portions directly, although it leaves no unused end-of-day gap. It does not use overflow, so it provides a distinct way to express an overnight opening window.
These two approaches should not be mixed without a clear data policy. If the same overnight interval were represented through both overflow and split day definitions, the package's merge behavior would matter. The README does not document that edge case, so the safer design is to choose one representation per business schedule and preserve it consistently in storage and imports.
Creating an OpeningHours object and checking a first date
The README links the package's Packagist page and identifies the package as `spatie/opening-hours`, but it does not provide a Composer installation command. The installation excerpt is therefore left out rather than reconstructing an unsupported command. After installing through the documented package channel, the first real use is to create an object and query it with PHP's `DateTime`.
Import the class and define a weekly schedule with one dated exception. The `2016-12-25` entry replaces Christmas Day's normal schedule with no opening hours.
use Spatie\OpeningHours\OpeningHours;
$openingHours = OpeningHours::create([
'monday' => ['09:00-12:00', '13:00-18:00'],
'sunday' => [],
'exceptions' => [
'2016-12-25' => [],
],
]);A normal weekday can then be checked against the recurring schedule, while Christmas can be checked against the exception.
$openingHours->isOpenOn('monday');
$openingHours->isOpenOn('2016-12-25');The README states that the first call returns `true` and the second returns `false`. That gives an integration a small, concrete check: first verify the recurring Monday result, then verify that the dated exception closes Christmas Day. Time-zone configuration is not shown in the example, and the README does not state how time zones are resolved, so that must be verified in the installed version rather than inferred.
Current-range queries, display data, and business semantics
The object can provide information for a status display rather than only a boolean. `currentOpenRange($now)` returns a range when the business is open. The README then uses `$range->start()` and `$range->end()` to report when the current period began and when it closes. When no current range exists, `previousClose($now)` and `nextOpen($now)` provide surrounding schedule transitions.
Schedule definitions can also carry arbitrary metadata through `data`. A day may contain a day-level value, while an individual period can carry its own value. The README states that `data` can contain any kind of value and demonstrates associative structures using `hours` and `data` keys. This supports labels such as `Typical Monday` or `Extra on Tuesday evening`, and the examples show that metadata can be retrieved from `forDay()`, `forDate()`, or a particular period.
That flexibility is useful, but it also leaves validation to the application. The package can preserve a string or a nested array, yet the README does not define a schema for those values. An application should decide whether data is display text, an internal identifier, translated text, or structured business metadata before loading schedule input. If multiple views need different labels, storing all presentation concerns in one `data` field may create coupling.
The query methods also return different shapes, including an `OpeningHoursForDay` object, arrays keyed by day or date, and combined schedules. Consumers should not treat them as interchangeable arrays. Reading the documented return type for each display component will prevent a regular schedule from bypassing exceptions.
Limits, maintenance, and the application-owned alternative
The strongest limitation is scope. spatie/opening-hours documents rule creation and querying, but the README does not document persistence, an editing interface, translations, time-zone behavior, schedule validation, conflict precedence, rollback, or an API for importing schedules. It is therefore the wrong tool when those capabilities are requirements rather than incidental implementation details. It is also not a booking system: the package can report whether a business is open, but the available documentation does not describe reservations or capacity management.
A real alternative is to persist opening periods as normalized application database records, such as weekday, start time, end time, and effective date, then place scheduling queries in a domain service. That approach makes persistence, auditing, editing permissions, and transaction boundaries explicit. Its cost is that developers must implement exception resolution, overnight merging, and display formatting themselves. spatie/opening-hours offers a focused PHP schedule object instead, reducing that domain code but requiring the surrounding application to load and validate the definition.
Maintenance appears current within the documented release history. The repository is not archived, the last push was on 2026-09-27, and release `4.2.3` was published on 2026-09-27. Releases `4.2.2` and `4.2.1` are also listed, but their presence does not by itself establish upgrade compatibility. The project uses the MIT license. The `LICENSE.md` file governs use, modification, distribution, and warranty terms, so organizations should inspect that file for their compliance process rather than treating the package as unrestricted.
Upgrade cost cannot be quantified from the README. Before changing from an earlier major version, review `CHANGELOG.md` and the matching release notes, then run the package's own test suite against schedule definitions that include recurring holidays, full-date exceptions, metadata, and overnight intervals.
Editorial conclusion
Adopt spatie/opening-hours when a PHP application needs a programmatic model for recurring weekly hours, holiday exceptions, and overnight ranges. Do not choose it as a complete scheduling platform without first confirming requirements it does not document, including persistence, localization, and operational rollback. The first integration check should confirm that your actual date formats, exception precedence, metadata, and cross-midnight schedules produce the expected results.
Frequently asked questions
How do I create opening hours in spatie/opening-hours?
Call `OpeningHours::create()` with an array of weekday names and time ranges. Add dated or recurring overrides through the `exceptions` key.
How does spatie/opening-hours handle schedules that cross midnight?
Set `overflow` to `true` during construction to let ranges such as `20:00-03:00` continue into the following day. The package can then check the previous day's schedule for the continuation.
Can spatie/opening-hours store labels with a schedule?
Yes. A day or individual period can include a `data` entry containing any value, including a string or an associative array. The README demonstrates retrieving that value from day and date results.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/spatie-opening-hours)Star history chart for your README
[](https://hysenlabs.com/projects/spatie-opening-hours)Was this useful?