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

Holidays Laravel Package

spatie/holidays

Spatie's PHP package calculates public holidays for a country. Call Holidays::for('be')->get() to receive Holiday objects with localized names, CarbonImmutable dates, and types. Supports many countries via ISO 3166-1 alpha-2 codes or country instances. Requires PHP 8.4+.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit. A framework-agnostic PHP utility library: a static entry point (Holidays::for()), immutable value objects (Holiday with CarbonImmutable dates, a HolidayType enum, and JsonSerializable), and a Country abstraction with per-country subclasses. There is no service provider, no config file, no persistence, and no HTTP surface. It belongs at the domain-utility layer — callable from controllers, queued jobs, Artisan commands, and API resources identically. The JsonSerializable contract means holiday data can be exposed directly in a HolidayResource without a mapping layer, and CarbonImmutable is already resident in any Laravel stack, so no competing date abstraction is introduced.

Extension model. The important architectural detail is CountryRegistry::MAP, a hardcoded constant mapping ISO codes to classes. Adding a new jurisdiction to the string-code path requires forking or an upstream PR. However, Holidays::for() also accepts a Country instance, so proprietary or unregistered calendars can subclass Country in application code and be passed in directly. Only Holidays::has() and ISO-code lookup are unavailable for those. This is a workable escape hatch but it argues for always resolving holidays through an app-owned adapter rather than calling the facade across the codebase — otherwise an upstream rename or later registration of the same country creates a collision.

Hard constraints. PHP 8.4+ is a genuine gating risk. Laravel 10 environments (PHP 8.1–8.3) cannot run current releases at all and would need to pin an older package major — which simultaneously pins older holiday data. That trade-off should be surfaced before scoping. The second constraint is supportedYearRange() on the lookup-table calendar traits (Islamic, Chinese, Indian, Nepali): dates are precomputed constants with an explicit horizon, and queries outside it throw InvalidYear. An explicit exception is the right failure mode, but it is a scheduled cliff that somebody must own.

Primary technical risk: correctness is a data problem, not a code problem. Holiday definitions change by decree — new statutory days, one-off national holidays, substitute-day rule changes. The package's correctness model is Pest snapshot tests per country, which lock current behaviour, not legal truth; a wrong date that is snapshotted stays wrong until someone challenges it. Data arrives via community PRs with no SLA. For any use with payroll, billing, or contractual SLA consequences, the package is an 80% accelerator and the final verification is yours.

Key questions.

  • Which countries and ISO 3166-2 regions must be supported at launch, and are all of them in the registry today?
  • Who signs off on legal/payroll correctness, and against which authoritative source per country?
  • Does the HolidayType taxonomy (National/Regional/Religious/Observed/Banked) map onto our domain categories, or does it need reinterpretation?
  • What is our policy for one-off or emergency holidays, and how will we detect that an installed version is stale?
  • Do we need substitute/observed-day logic that differs from the package's per-country HasObservedHolidays helpers?
  • What is the horizon-year plan for lookup-table countries, and is there monitoring on InvalidYear?
  • Is date-only granularity sufficient, or does an hourly or multi-timezone workforce need timezone-aware holiday windows?

Integration Approach

Stack fit. composer require spatie/holidays; nothing to publish or configure. Verify the Carbon constraint resolves against the application's locked nesbot/carbon before committing — Carbon major bumps are the most likely dependency conflict.

Migration path. There is no schema change and no data migration. If an in-house holiday table or hardcoded calendar already exists, run the package in shadow mode for one full calendar year: compute holidays from both sources in a non-blocking job, log deltas, and only cut over after the delta set is empty or explained. This is cheap and converts the largest unknown (data accuracy for our countries) into an observed fact rather than an assumption.

Adapter and caching. Wrap the facade in a single application service (e.g. HolidayCalendar) that exposes only the operations the product needs — forCountryYear(), isBusinessDay(), nextBusinessDay(), longWeekends() — and returns app-owned DTOs. Benefits: one place to add caching, one place to handle unsupported-country fallback, one place to swap the vendor if needed. Cache keyed on country + region + year + package version; a country-year calendar is immutable once law settles, so a Redis or DB-backed cache with a long TTL is safe and busted automatically by the version component on composer update.

Fallback for uncovered jurisdictions. Register an app-level map of Country subclasses for anything not in the registry, and make HolidayCalendar resolve instance-first rather than code-first. Never let an InvalidCountry/InvalidRegion exception escape into a user-facing path.

Sequencing.

  1. Inventory required countries/regions and check each against Holidays::has().
  2. Spot-check two years of output per country against an authoritative government source; record discrepancies.
  3. Build the adapter plus cache layer; put it behind a feature flag.
  4. Integrate one bounded consumer first (e.g. support-desk SLA timers or a calendar UI), not payroll.
  5. Add exception monitoring on InvalidYear/InvalidRegion, and a release-watch on the upstream changelog.
  6. Expand to financial-grade consumers only after a full shadow-mode cycle.

Locale handling. Translation files are JSON keyed by the original holiday name with silent fallback. Treat missing translations as a product defect, not a non-event — untranslated Dutch or German strings will surface in a French UI. Add a test that asserts no fallback occurs for our supported locales.

Operational Impact

Maintenance. The package's release cadence effectively is our data refresh cadence. Upgrading composer is how a newly legislated holiday reaches production; there is no separate data feed. That means the package must be on the routine dependency-upgrade train with an owner, not parked at a pinned version. Budget a small recurring cost per upgrade to re-run the spot-check and diff the snapshot output for our countries. The changelog is release-oriented rather than data-oriented, so diffs of actual holiday output are worth generating ourselves.

Support. Spatie is a well-established commercial OSS shop with a strong Laravel track record and a documented security policy, so code-level support risk is low. Data-level support is community-driven: fixes arrive as PRs from contributors, with no SLA and no escalation path. The reported dependent count is effectively zero, which should be read as limited evidence of production use at our scale rather than a defect — but it does mean we will not have a body of public war stories to lean on when something looks wrong.

Scaling. Compute is trivial: dozens of date calculations per country per year. The only scaling concern is fan-out — per-employee, per-country, per-pay-period loops in reporting jobs. Precompute the country-year calendar once per cache key and iterate the cached array; that turns an O(n) recomputation into O(1) lookups. Do not call Holidays::for() inside an inner loop.

Failure modes to instrument.

  1. Unsupported country or region — exception or empty result; must degrade to "no holidays known" with a warning, never to a wrong business-day calculation.
  2. Year beyond the lookup-table horizon — InvalidYear on Islamic/Chinese/Indian/Nepali calendars. Loud failure is correct; ensure the exception is not swallowed by a broad catch. Alert before the horizon, not after.
  3. Silent locale fallback — untranslated holiday names leaking into a localized UI. Assert against it in tests.
  4. Data staleness — a newly declared public holiday missing from the installed version, producing a wrong payroll or SLA result with no runtime signal. Only a release-watch and a periodic source re-verification catch this.
  5. Observed-day double counting — the package emits observed days as separate Observed entries; if our business rule differs (or we also apply our own roll-forward), we will count twice or miss the substitute day.
  6. Timezone drift — values are date-only; isTodayHoliday() evaluated against now() in a different app timezone can be off by a day in distributed or multi-region deployments.
  7. Runtime floor — PHP 8.4 blocks older environments and interacts badly with pinned-package compromises.

Ramp-up. Low. A single developer can integrate and ship a read-only holiday display in under a day; the API surface is small, the README is complete with worked examples, and the snapshot tests double as a record of expected values. Realistically, 80% of the effort sits in jurisdiction inventory, source verification, and the adapter/cache layer — not in learning the package. That asymmetry is the core planning insight: this is a cheap integration and a moderately expensive validation project, and the validation work is what determines whether it is safe for money-touching or contract-touching flows.

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.
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels
wazza/laravel-db-encryption
hokny/laravel-generator
vonsogt/backup
jengo/queues
jeremykenedy/laravel-observability
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums