Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Opening Hours Laravel Package

spatie/opening-hours

spatie/opening-hours lets you define business opening hours as a weekly schedule plus exceptions, then query whether you’re open or closed on a day or specific date and present times per day. It also works with Carbon via cmixin/business-time.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit: spatie/opening-hours is a framework-agnostic, dependency-light PHP library, not a Laravel package in the traditional sense — there is no service provider, facade, config file, or migration. That is a feature, not a gap: the OpeningHours aggregate (with Time, TimeRange, and OpeningHoursForDay value objects) is a clean domain primitive that drops into a Laravel service layer without imposing persistence, routing, or container conventions. For a TPM, this means it can be adopted as a bounded piece of business logic — a "schedule resolver" — and wrapped with whatever Laravel idioms the application already uses (a repository, a singleton binding, a cast, or a facade of your own).

Integration feasibility: High. Installation is a single Composer require. The schedule is a plain array, which maps naturally to a JSON column on an existing model (store, location, branch, provider) or to a separate schedules table. Hydration is OpeningHours::create($array, $timezone). Query methods (isOpenAt, isOpenOn, nextOpen, nextClose, currentOpenRange, forWeek, forDate) cover the overwhelmingly common product requirements — "Open now" badges, next-open messaging, weekly display, holiday exceptions — without custom date math. Input/output timezone separation is a genuinely useful capability for multi-region tenants, and the optional Carbon integration via cmixin/business-time lets existing Carbon-centric codebases stay idiomatic.

Technical risk: Moderate but contained, concentrated in a few areas:

  • Timezone and DST semantics. The library assumes passed DateTime objects match the schedule timezone unless you configure conversion. DST transitions around overnight ranges (overflow, 24:00) are the classic source of subtle bugs and need explicit test cases.
  • Filter performance and determinism. Filters are closures evaluated per date with no caching; the README explicitly warns about this. In a Laravel app this is an invitation to put database or API calls inside a filter — a latent performance and correctness hazard that should be prohibited by convention.
  • Overlapping ranges throw by default. Acceptable, but it means user-entered schedules need validation/merge handling (mergeOverlappingRanges, createAndMergeOverlappingRanges) before construction.
  • Ecosystem signal is weak. The metadata shows zero dependents, which conflicts with a 1.7k-star, widely-downloaded Spatie package. Treat that as a data-quality artifact to verify rather than a popularity signal. The reported last release date is also anomalous and should be confirmed against Packagist before pinning a version.

Key questions:

  1. Which PHP and Carbon versions are supported by the version we pin, and does that align with our Laravel runtime?
  2. Do we need multi-location/per-tenant schedules, and does one aggregate per location scale for our query patterns?
  3. Are nextOpen/nextClose searches bounded when a schedule is permanently closed, or can they iterate indefinitely?
  4. What is our policy for user-authored exceptions — stored as data, or generated by our own filters?
  5. Do we need audit history on schedule changes (regulatory/consumer-facing hours)? The package has no notion of versioning.

Integration Approach

Stack fit: Works with any Laravel version on a supported PHP runtime; no framework coupling to fight. The natural placement is app/Domain/Schedule/ with a thin ScheduleResolver service that accepts a model, builds the OpeningHours object, and exposes application-shaped methods (isOpenNow(), nextOpeningLabel()). Eloquent should never be aware of the library beyond a custom cast or accessor that serializes/deserializes the JSON schedule.

Migration path: None required for the package itself. The real work is our own schema: a JSON schedule column plus timezone on the owning entity, and an exceptions structure (either embedded in the JSON or a related table if exceptions need independent editing, approval, or audit). A three-step path is recommended: (1) add the column and hydrate existing manual hours into the array format, (2) read exclusively through the resolver while keeping the legacy display path as a fallback, (3) retire the legacy path once parity is confirmed across a full week plus a holiday.

Compatibility: Confirm Carbon 2 vs. 3 alignment early, since Carbon is the most likely transitive-version collision in a large Laravel app. If the app already uses cmixin/business-time, validate that the added Carbon macros don't conflict. Output timezone conversion means API responses should be explicit about which timezone they return — pick one convention and enforce it in resources.

Sequencing:

  1. Spike: hydrate a representative schedule (including overflow, recurring exceptions, and a DST boundary) and assert outputs.
  2. Persistence and validation layer, including a rule that merges or rejects overlapping ranges at write time.
  3. Resolver service with request-scoped memoization.
  4. Caching layer for hot reads ("open now" on listing pages) keyed by entity + date + coarse time bucket.
  5. Presentation (API resources, weekly UI grouping via forWeekCombined/forWeekConsecutiveDays).
  6. Edge-case hardening and observability.

Operational Impact

Maintenance: Low surface area and an MIT license make this cheap to own. The library has been stable for years with a small, well-documented API. Principal maintenance burden is not the package but the schedule data model we build around it. Pin the version, review the changelog on upgrade, and keep the resolver as the only call site so a future swap (or fork) is a one-file change.

Support: Community-only via GitHub issues; no SLA, no commercial backing. Given the small API and mature test suite this is acceptable for a non-critical feature, but it should be classified accordingly — this package is not appropriate as the sole authority for something like billing eligibility or regulatory compliance without our own test coverage and fallback logic.

Scaling: Computation is pure CPU and cheap per call, but the cost accumulates per entity per request. Two mitigations matter: memoize the OpeningHours object within a request (construction parses the full array each time), and cache "open now" / "next open" results for high-traffic listing pages, invalidating on schedule write and at time-bucket boundaries. Avoid filters entirely in hot paths; if dynamic exceptions are needed, precompute them into concrete exception arrays. Querying thousands of locations for "open now" in a single page should be modeled as a batch resolve with caching, not N uncached constructions.

Failure modes: The realistic ones are (a) exceptions thrown on overlapping or malformed ranges during user input or data import — catch at the boundary and surface as validation errors; (b) incorrect results around DST and overnight overflow ranges — cover with fixed-date tests; (c) timezone mismatch between stored schedule and request context producing confidently wrong "Open now" badges — enforce a single timezone convention and log mismatches; (d) unbounded or slow behavior from filters — disallow by convention and code review; (e) null returns from nextOpen/previousClose when a schedule is entirely closed — callers must handle nulls rather than assume a DateTime. A dashboard counter for "open now" queries returning null is a cheap early-warning signal.

Ramp-up: Very low. One aggregate class, three value objects, and a README that covers nearly every method with examples. A mid-level engineer can be productive in half a day; the real learning curve is the schedule domain (exception precedence, overflow, timezone conversion), which is best captured as a short internal ADR plus a fixture-based test suite rather than left to the upstream docs.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
oxygen/data
oxygen/core
jengo/base
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin
artisan-build/community-prompts
vleroy/smart
ayles-software/laravel-australian-public-holidays
cboxdk/laravel-id-spatie
cboxdk/laravel-id-billing
cboxdk/laravel-id-billing-bridge
simple-as-fuck/php-performance-log
hsluv/hsluv
seampact/codegen
seampact/hooks
seampact/ir
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table