Skip to content

spatie/holidays

Repository files navigation

Calculate public holidays for a country

Latest Version on Packagist Tests Total Downloads

This package can calculate public holidays for a country.

use Spatie\Holidays\Holidays;

$holidays = Holidays::for('be')->get();

$holidays[0]->name; // 'Nieuwjaar'
$holidays[0]->date; // CarbonImmutable('2024-01-01')
$holidays[0]->type; // HolidayType::National

Support us

We invest a lot of resources into creating best in class open source packages. You can support us by buying one of our paid products.

We highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using. You'll find our address on our contact page. We publish all received postcards on our virtual postcard wall.

Installation

You can install the package via composer:

composer require spatie/holidays

This package requires PHP 8.4+.

Supported countries

We support the countries listed in this directory. If you want to add a country, please create a pull request. See Adding a new country for a guide.

Usage

You can get all holidays for a country by using the get method. It returns an array of Holiday objects.

use Spatie\Holidays\Holidays;

$holidays = Holidays::for('be')->get();

foreach ($holidays as $holiday) {
    $holiday->name; // 'Nieuwjaar'
    $holiday->date; // CarbonImmutable instance
    $holiday->type; // HolidayType::National
}

You can pass a country instance or an ISO 3166-1 alpha-2 code:

use Spatie\Holidays\Countries\Belgium;

$holidays = Holidays::for(Belgium::make())->get();
$holidays = Holidays::for('be')->get();

The Holiday object implements JsonSerializable:

json_encode($holidays[0]);
// {"name":"Nieuwjaar","date":"2024-01-01","type":"national","region":null}

Getting holidays for a specific year

$holidays = Holidays::for(country: 'be', year: 2024)->get();

Getting holidays between two dates

The getInRange method returns all holidays between two dates (inclusive). Dates are swappable — the lower date is always used as the start.

$holidays = Holidays::for('be')->getInRange('2024-01-01', '2024-12-31');

You can also use shorthand formats:

$holidays = Holidays::for('be')->getInRange('2024', '2025');     // full years
$holidays = Holidays::for('be')->getInRange('2024-06', '2025-05'); // year-month

Getting holidays in a specific language

$holidays = Holidays::for(country: 'be', locale: 'fr')->get();

If no translation file exists for the given locale, the original holiday names are returned.

Regional holidays

Some countries have region-specific holidays. You can pass a region code to the for method:

$holidays = Holidays::for('de', year: 2024, region: 'DE-BW')->get();

Or use the country class directly:

use Spatie\Holidays\Countries\Germany;

$holidays = Holidays::for(Germany::make('DE-BW'), year: 2024)->get();

To discover which regions a country supports:

use Spatie\Holidays\Countries\Germany;

Germany::regions(); // ['DE-BW', 'DE-BY', 'DE-BE', ...]

Countries that support regions: Australia, Bosnia and Herzegovina, France, Germany, Malaysia, Spain, Switzerland.

Determining if a date is a holiday

Holidays::for('be')->isHoliday('2024-01-01'); // true

Determining if today is a holiday

Holidays::for('be')->isTodayHoliday(); // true or false

Getting upcoming holidays

$holidays = Holidays::for('be')->getUpcoming(3);

// Returns the next 3 upcoming holidays
foreach ($holidays as $holiday) {
    echo $holiday->date; // CarbonImmutable
    echo $holiday->name;
}

Getting long weekends

$longWeekends = Holidays::for('de', 2024)->getLongWeekends();

// With custom minimum days (default: 4)
$longWeekends = Holidays::for('de', 2024)->getLongWeekends(3);

foreach ($longWeekends as $weekend) {
    echo $weekend->startDate; // First day of the long weekend
    echo $weekend->endDate;   // Last day of the long weekend
    echo $weekend->dayCount;  // Number of days
    echo $weekend->holidays;  // Array of Holiday objects
}

Getting the name of a holiday

Holidays::for('be')->getName('2024-01-01'); // 'Nieuwjaar'

Determining whether a country is supported

Holidays::has('be'); // true
Holidays::has('unknown'); // false

Adding a new country

This is a community driven package. If you find any errors, please create a pull request with the fix, or at least open an issue.

Basic country

Create a new class in src/Countries that extends Country. At minimum, you need to implement countryCode() and allHolidays():

use Spatie\Holidays\Countries\Country;
use Spatie\Holidays\Holiday;
use Spatie\Holidays\HolidayType;

class MyCountry extends Country
{
    public function countryCode(): string
    {
        return 'xx'; // ISO 3166-1 alpha-2 code
    }

    protected function allHolidays(int $year): array
    {
        return [
            Holiday::national("New Year's Day", "{$year}-01-01"),
            Holiday::national('Christmas', "{$year}-12-25"),
        ];
    }
}

The Holiday::national() helper accepts both strings and CarbonImmutable instances. For Easter-based holidays, use the easter() or orthodoxEaster() helpers:

protected function allHolidays(int $year): array
{
    $easter = $this->easter($year);

    return [
        Holiday::national('Good Friday', $easter->subDays(2)),
        Holiday::national('Easter Monday', $easter->addDay()),
    ];
}

For relative dates, use CarbonImmutable::parse():

Holiday::national('Labor Day', CarbonImmutable::parse("first monday of September {$year}"));

If your country defines holidays in a non-English language, override defaultLocale():

protected function defaultLocale(): string
{
    return 'de';
}

Register your country

In src/CountryRegistry.php, add your ISO country code and country class to the MAP constant.

Regional holidays

If your country has region-specific holidays, implement the HasRegions interface:

use Spatie\Holidays\Contracts\HasRegions;
use Spatie\Holidays\Exceptions\InvalidRegion;

class MyCountry extends Country implements HasRegions
{
    protected function __construct(protected ?string $region = null)
    {
        if ($region !== null && ! in_array($region, static::regions())) {
            throw InvalidRegion::notFound($region);
        }
    }

    public static function regions(): array
    {
        return ['XX-A', 'XX-B', 'XX-C'];
    }

    public function region(): ?string
    {
        return $this->region;
    }

    protected function allHolidays(int $year): array
    {
        return array_merge(
            $this->nationalHolidays($year),
            $this->regionHolidays($year),
        );
    }

    protected function regionHolidays(int $year): array
    {
        return match ($this->region) {
            'XX-A' => ['Regional Day' => CarbonImmutable::createFromDate($year, 6, 1)],
            default => [],
        };
    }
}

Observed holidays

If your country moves holidays that fall on a weekend to the next weekday, use the HasObservedHolidays trait:

use Spatie\Holidays\Concerns\HasObservedHolidays;
use Spatie\Holidays\Holiday;

class MyCountry extends Country
{
    use HasObservedHolidays;

    protected function allHolidays(int $year): array
    {
        $holidays = [
            Holiday::national("New Year's Day", "{$year}-01-01"),
        ];

        // Example: if New Year's Day falls on a weekend, add an observed day
        $newYear = $this->sundayToNextMonday($holidays[0]->date);
        if ($newYear !== null) {
            $holidays[] = Holiday::observed("New Year's Day (Observed)", $newYear);
        }

        return $holidays;
    }
}

The trait provides these methods (each returns null if no shift applies):

  • weekendToNextMonday(CarbonInterface $date) — shifts Saturday/Sunday to Monday
  • sundayToNextMonday(CarbonInterface $date) — shifts Sunday to Monday
  • observedChristmasDay(CarbonInterface $date) — Saturday to Monday, Sunday to Tuesday
  • observedBoxingDay(CarbonInterface $date) — Saturday to Monday, Sunday to Tuesday

Holiday types

The Holiday class supports different holiday types:

use Spatie\Holidays\Holiday;
use Spatie\Holidays\HolidayType;

Holiday::national('New Year', "{$year}-01-01");              // Default type
Holiday::regional('Regional Day', "{$year}-06-01", 'XX-RE'); // With region
Holiday::religious('Easter', $easterDate);                   // Religious holidays
Holiday::observed('Observed Day', $observedDate);             // Observed holidays
Holiday::banked('Bank Holiday', "{$year}-12-25");           // Bank holidays

Available types in HolidayType enum: National, Regional, Religious, Observed, Banked.

Calendar systems

For countries that use Islamic, Chinese, Indian, or Nepali calendar dates, use the corresponding calendar trait. These traits rely on precomputed lookup tables defined as protected const arrays on your country class:

use Spatie\Holidays\Calendars\IslamicCalendar;
use Spatie\Holidays\Contracts\Islamic;

class MyCountry extends Country implements Islamic
{
    use IslamicCalendar;

    protected const eidAlFitr = [
        2024 => '04-10',
        2025 => '03-30',
        // ...
    ];

    protected const eidAlAdha = [
        2024 => '06-16',
        2025 => '06-06',
        // ...
    ];
}

Countries with lookup tables must declare their supported year range:

protected function supportedYearRange(): array
{
    return [2024, 2037]; // [min, max] based on your lookup data
}

For multi-day holidays (like Eid), use convertPeriods() to expand a CarbonPeriod into individual named days:

$this->convertPeriods('Eid al-Fitr', $year, $this->eidAlFitr($year)[0], includeEve: true);

Available calendar traits: IslamicCalendar, ChineseCalendar, IndianCalendar, NepaliCalendar.

Translations

To add translations for a country, create a JSON file at lang/{countryCode}/{locale}/holidays.json:

{
    "New Year's Day": "Jour de l'An",
    "Christmas": "Noël"
}

The keys must match the holiday names returned by allHolidays().

Testing

  1. Create a test file in tests/Countries/:
use Carbon\CarbonImmutable;
use Spatie\Holidays\Holidays;

it('can calculate my country holidays', function () {
    CarbonImmutable::setTestNow('2024-01-01');

    $holidays = Holidays::for(country: 'xx')->get();

    expect($holidays)
        ->toBeArray()
        ->not()->toBeEmpty();

    expect(formatDates($holidays))->toMatchSnapshot();
});
  1. Run vendor/bin/pest --update-snapshots to generate the snapshot.
  2. Verify the generated snapshot in tests/.pest/snapshots/ is correct.

Testing

composer test

Changelog

Please see CHANGELOG for more information on what has changed recently.

Upgrading

Please see UPGRADING for how to upgrade to a new major version.

Contributing

Please see CONTRIBUTING for details.

Security Vulnerabilities

Please review our security policy on how to report security vulnerabilities.

Credits

License

The MIT License (MIT). Please see License File for more information.

About

Calculate which days you don't have to work!

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Sponsor this project

 

Contributors

Languages