diff --git a/.github/workflows/fix-php-code-style-issues.yml b/.github/workflows/fix-php-code-style-issues.yml index cd4239c24..30e62c774 100644 --- a/.github/workflows/fix-php-code-style-issues.yml +++ b/.github/workflows/fix-php-code-style-issues.yml @@ -1,9 +1,6 @@ name: Fix PHP code style issues -on: - push: - paths: - - '**.php' +on: [push] permissions: contents: write diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml index 60ce66b64..e171b0463 100644 --- a/.github/workflows/phpstan.yml +++ b/.github/workflows/phpstan.yml @@ -1,6 +1,6 @@ name: PHPStan -on: [push] +on: [push, pull_request] jobs: phpstan: diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 14315473c..77a34a44d 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -1,13 +1,6 @@ name: Tests -on: - push: - paths: - - '**.php' - - '.github/workflows/run-tests-pest.yml' - - 'phpunit.xml.dist' - - 'composer.json' - - 'composer.lock' +on: [push, pull_request] jobs: test: diff --git a/README.md b/README.md index 12f5b0ff3..44af97b77 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ use Spatie\Holidays\Holidays; // returns an array of Belgian holidays // for the current year -$holidays = Holidays::for('be')->get(); +$holidays = Holidays::for('be')->get(); ``` ## Support us @@ -40,21 +40,21 @@ You can get all holidays for a country by using the `get` method. ```php use Spatie\Holidays\Holidays; +use Spatie\Holidays\Countries\Belgium; // returns an array of Belgian holidays // for the current year -$holidays = Holidays::for('be')->get(); +$holidays = Holidays::for(Belgium::make())->get(); ``` -Alternatively, you could also pass an instance of `Country` to the `for` method. +Alternatively, you could also pass an ISO code to the `for` method. ```php use Spatie\Holidays\Holidays; -use Spatie\Holidays\Countries\Belgium; // returns an array of Belgian holidays // for the current year -$holidays = Holidays::for(Belgium::make())->get(); +$holidays = Holidays::for('be')->get(); ``` ### Getting holidays for a specific year @@ -87,6 +87,40 @@ use Spatie\Holidays\Holidays; Holidays::for('be')->getName('2024-01-01'); // Nieuwjaar ``` +## Contributing a new country + +If you want to add a new country, you can create a pull request. + +1. Create a new class in the `Countries` directory. It should extend the `Country` class. +2. Add a test for the new country in the `tests` directory. +3. Run the tests so a snapshot gets created. +4. Verify the result in the newly created snapshot is correct. + +In case your country has specific rules for calculating holidays, +for example region specific holidays, you can pass this to the constructor of your country class. + +```php +$holidays = Holidays::for(Austria::make(region: 'de-bw'))->get(); +``` + +The value, `de-bw`, will be passed to the region parameter of the contructor of a country. + +```php +class Austria extends Country +{ + protected function __construct( + protected ?string $region = null, + ) { + } + + protected function allHolidays(int $year): array + { + // Here you can use $this->region (or other variables) to calculate holidays + } +``` + +Please see [CONTRIBUTING](https://github.com/spatie/.github/blob/main/CONTRIBUTING.md) for more details. + ## Testing ```bash @@ -97,10 +131,6 @@ composer test Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently. -## Contributing - -Please see [CONTRIBUTING](https://github.com/spatie/.github/blob/main/CONTRIBUTING.md) for details. - ## Security Vulnerabilities Please review [our security policy](../../security/policy) on how to report security vulnerabilities. diff --git a/composer.json b/composer.json index aa1da0465..91002ed8c 100644 --- a/composer.json +++ b/composer.json @@ -25,9 +25,11 @@ "ext-calendar": "*" }, "require-dev": { + "laravel/prompts": "^0.1.15", "pestphp/pest": "^2.31", "phpstan/phpstan": "^1.10.56", - "spatie/ray": "^1.40.1" + "spatie/ray": "^1.40.1", + "symfony/var-dumper": "^6.4" }, "autoload": { "psr-4": { diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 40f94f7ef..51b9330fc 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,9 +1,9 @@ parameters: ignoreErrors: - - message: "#^Method Spatie\\\\Holidays\\\\Countries\\\\Belgium\\:\\:allHolidays\\(\\) should return array\\ but returns array\\\\.$#" + message: "#^Unreachable statement \\- code above always terminates\\.$#" count: 1 - path: src/Countries/Belgium.php + path: src/Countries/Brazil.php - message: "#^Argument of an invalid type array\\\\|false supplied for foreach, only iterables are supported\\.$#" @@ -11,7 +11,7 @@ parameters: path: src/Countries/Country.php - - message: "#^Method Spatie\\\\Holidays\\\\Countries\\\\Country\\:\\:get\\(\\) should return array\\ but returns array\\\\.$#" + message: "#^Method Spatie\\\\Holidays\\\\Countries\\\\Country\\:\\:get\\(\\) should return array\\ but returns array\\\\.$#" count: 1 path: src/Countries/Country.php @@ -20,11 +20,6 @@ parameters: count: 1 path: src/Countries/Country.php - - - message: "#^Method Spatie\\\\Holidays\\\\Countries\\\\Hungary\\:\\:allHolidays\\(\\) should return array\\ but returns array\\\\.$#" - count: 1 - path: src/Countries/Hungary.php - - message: "#^Cannot call method isSunday\\(\\) on Carbon\\\\CarbonImmutable\\|false\\.$#" count: 1 @@ -44,3 +39,8 @@ parameters: message: "#^Parameter \\#1 \\$callback of function array_map expects \\(callable\\(Carbon\\\\CarbonImmutable\\)\\: mixed\\)\\|null, Closure\\(string\\)\\: non\\-falsy\\-string given\\.$#" count: 1 path: src/Holidays.php + + - + message: "#^Property Spatie\\\\Holidays\\\\Holidays\\:\\:\\$holidays \\(array\\\\) does not accept array\\\\.$#" + count: 1 + path: src/Holidays.php diff --git a/playground.php b/playground.php new file mode 100644 index 000000000..c53856a26 --- /dev/null +++ b/playground.php @@ -0,0 +1,31 @@ +get()) + ->map(fn (array $holiday) => [ + 'name' => $holiday['name'], + 'date' => $holiday['date']->format('Y-m-d'), // @phpstan-ignore-line + ])->toArray(); + +dd($result); diff --git a/src/Countries/Andorra.php b/src/Countries/Andorra.php index 19e342ca5..51615e690 100644 --- a/src/Countries/Andorra.php +++ b/src/Countries/Andorra.php @@ -11,7 +11,6 @@ public function countryCode(): string return 'ad'; } - /** @return array */ protected function allHolidays(int $year): array { return array_merge([ diff --git a/src/Countries/Austria.php b/src/Countries/Austria.php index c8eea5d62..73fb61285 100644 --- a/src/Countries/Austria.php +++ b/src/Countries/Austria.php @@ -6,12 +6,16 @@ class Austria extends Country { + protected function __construct( + public ?string $region = null + ) { + } + public function countryCode(): string { return 'at'; } - /** @return array */ protected function allHolidays(int $year): array { return array_merge([ @@ -34,7 +38,7 @@ protected function variableHolidays(int $year): array ->setTimezone('Europe/Vienna'); return [ - 'Ostermontag' => $easter->addDay(1), + 'Ostermontag' => $easter->addDay(), 'Christi Himmelfahrt' => $easter->addDays(39), 'Pfingstmontag' => $easter->addDays(50), 'Fronleichnam' => $easter->addDays(60), diff --git a/src/Countries/Belgium.php b/src/Countries/Belgium.php index f5d268db5..af52b6810 100644 --- a/src/Countries/Belgium.php +++ b/src/Countries/Belgium.php @@ -11,7 +11,6 @@ public function countryCode(): string return 'be'; } - /** @return array */ protected function allHolidays(int $year): array { return array_merge([ diff --git a/src/Countries/Brazil.php b/src/Countries/Brazil.php index df13fedc0..89d08aadb 100644 --- a/src/Countries/Brazil.php +++ b/src/Countries/Brazil.php @@ -11,9 +11,10 @@ public function countryCode(): string return 'br'; } - /** @return array */ protected function allHolidays(int $year): array { + throw new \Exception('Not implemented yet.'); + return array_merge([ 'Dia de Ano Novo' => '01-01', 'Dia de Tiradentes' => '04-21', diff --git a/src/Countries/Country.php b/src/Countries/Country.php index b99262a5c..8b75b8dfd 100644 --- a/src/Countries/Country.php +++ b/src/Countries/Country.php @@ -13,7 +13,7 @@ abstract public function countryCode(): string; /** @return array */ abstract protected function allHolidays(int $year): array; - /** @return array */ + /** @return array */ public function get(int $year): array { $this->ensureYearCanBeCalculated($year); @@ -37,7 +37,7 @@ public function get(int $year): array public static function make(): static { - return new static(); + return new static(...func_get_args()); } public static function find(string $countryCode): ?Country diff --git a/src/Countries/Denmark.php b/src/Countries/Denmark.php new file mode 100644 index 000000000..66d2113a6 --- /dev/null +++ b/src/Countries/Denmark.php @@ -0,0 +1,46 @@ + '01-01', + 'Juleaften' => '12-24', + 'Juledag' => '12-25', + 'Anden Juledag' => '12-26', + ], $this->variableHolidays($year)); + } + + /** @return array */ + protected function variableHolidays(int $year): array + { + $easter = CarbonImmutable::createFromTimestamp(easter_date($year)) + ->setTimezone('Europe/Copenhagen'); + + $holidays = [ + 'Påskedag' => $easter->addDay(), + 'Skærtorsdag' => $easter->subDays(3), + 'Langfredag' => $easter->subDays(2), + 'Anden Påskedag' => $easter->addDays(2), + 'Kristi Himmelfartsdag' => $easter->addDays(39), + 'Pinse' => $easter->addDays(49), + 'Anden Pinsedag' => $easter->addDays(50), + ]; + + if ($year < 2024) { + $holidays['Store Bededag'] = $easter->addDays(26); + } + + return $holidays; + } +} diff --git a/src/Countries/Hungary.php b/src/Countries/Hungary.php index 8a6467455..15a46e997 100644 --- a/src/Countries/Hungary.php +++ b/src/Countries/Hungary.php @@ -11,7 +11,6 @@ public function countryCode(): string return 'hu'; } - /** @return array */ protected function allHolidays(int $year): array { return array_merge([ diff --git a/src/Countries/Netherlands.php b/src/Countries/Netherlands.php index c79223dd0..b694c25cc 100644 --- a/src/Countries/Netherlands.php +++ b/src/Countries/Netherlands.php @@ -11,7 +11,6 @@ public function countryCode(): string return 'nl'; } - /** @return array */ protected function allHolidays(int $year): array { return array_merge([ @@ -19,7 +18,6 @@ protected function allHolidays(int $year): array 'Bevrijdingsdag' => '05-05', '1e Kerstdag' => '12-25', '2e Kerstdag' => '12-26', - 'Oudejaarsdag' => '12-31', ], $this->variableHolidays($year)); } diff --git a/src/Countries/Portugal.php b/src/Countries/Portugal.php new file mode 100644 index 000000000..4525591d9 --- /dev/null +++ b/src/Countries/Portugal.php @@ -0,0 +1,42 @@ + '01-01', + 'Dia da Liberdade' => '04-25', + 'Dia do Trabalhador' => '05-01', + 'Dia de Portugal' => '06-10', + 'Assunção da Nossa Senhora' => '08-15', + 'Implantação da República' => '10-05', + 'Dia de Todos os Santos' => '11-01', + 'Restauração da Independência' => '12-01', + 'Imaculada Conceição' => '12-08', + 'Natal' => '12-25', + ], $this->variableHolidays($year)); + } + + /** @return array */ + protected function variableHolidays(int $year): array + { + $easter = CarbonImmutable::createFromTimestamp(easter_date($year)) + ->setTimezone('Europe/Lisbon'); + + return [ + 'Páscoa' => $easter, + 'Sexta-feira Santa' => $easter->subDays(2), + 'Corpo de Deus' => $easter->addDays(60), + ]; + } +} diff --git a/src/Countries/Zambia.php b/src/Countries/Zambia.php new file mode 100644 index 000000000..14112e6ea --- /dev/null +++ b/src/Countries/Zambia.php @@ -0,0 +1,44 @@ + '01-01', + 'International Womens Day' => '03-08', + 'Youth Day' => '03-12', + 'Birthday of Kenneth Kaunda' => '04-28', + 'Labour Day' => '05-01', + 'Africa Day' => '05-25', + 'Heroes Day' => '07-01', + 'Unity Day' => '07-02', + 'Farmers Day' => '08-01', + 'National Prayer Day' => '10-18', + 'Independence Day' => '10-24', + 'Christmas Day' => '12-25', + + ], $this->variableHolidays($year)); + } + + /** @return array */ + protected function variableHolidays(int $year): array + { + $easter = CarbonImmutable::createFromTimestamp(easter_date($year)) + ->setTimezone('Africa/Lusaka'); + + return [ + 'Good Friday' => $easter->subDays(2), + 'Easter Monday' => $easter->addDay(), + ]; + } +} diff --git a/tests/.pest/snapshots/Countries/DenmarkTest/it_can_calculate_danish_holidays.snap b/tests/.pest/snapshots/Countries/DenmarkTest/it_can_calculate_danish_holidays.snap new file mode 100644 index 000000000..b19f23c2d --- /dev/null +++ b/tests/.pest/snapshots/Countries/DenmarkTest/it_can_calculate_danish_holidays.snap @@ -0,0 +1,46 @@ +[ + { + "name": "Nyt\u00e5r", + "date": "2024-01-01" + }, + { + "name": "Sk\u00e6rtorsdag", + "date": "2024-03-28" + }, + { + "name": "Langfredag", + "date": "2024-03-29" + }, + { + "name": "P\u00e5skedag", + "date": "2024-04-01" + }, + { + "name": "Anden P\u00e5skedag", + "date": "2024-04-02" + }, + { + "name": "Kristi Himmelfartsdag", + "date": "2024-05-09" + }, + { + "name": "Pinse", + "date": "2024-05-19" + }, + { + "name": "Anden Pinsedag", + "date": "2024-05-20" + }, + { + "name": "Juleaften", + "date": "2024-12-24" + }, + { + "name": "Juledag", + "date": "2024-12-25" + }, + { + "name": "Anden Juledag", + "date": "2024-12-26" + } +] \ No newline at end of file diff --git a/tests/.pest/snapshots/Countries/NetherlandsTest/it_can_calculate_dutch_holidays.snap b/tests/.pest/snapshots/Countries/NetherlandsTest/it_can_calculate_dutch_holidays.snap index 3257dd423..7ba840be8 100644 --- a/tests/.pest/snapshots/Countries/NetherlandsTest/it_can_calculate_dutch_holidays.snap +++ b/tests/.pest/snapshots/Countries/NetherlandsTest/it_can_calculate_dutch_holidays.snap @@ -42,9 +42,5 @@ { "name": "2e Kerstdag", "date": "2024-12-26" - }, - { - "name": "Oudejaarsdag", - "date": "2024-12-31" } ] \ No newline at end of file diff --git a/tests/.pest/snapshots/Countries/PortugalTest/it_can_calculate_portuguese_holidays.snap b/tests/.pest/snapshots/Countries/PortugalTest/it_can_calculate_portuguese_holidays.snap new file mode 100644 index 000000000..13513781f --- /dev/null +++ b/tests/.pest/snapshots/Countries/PortugalTest/it_can_calculate_portuguese_holidays.snap @@ -0,0 +1,54 @@ +[ + { + "name": "Dia de Ano Novo", + "date": "2024-01-01" + }, + { + "name": "Sexta-feira Santa", + "date": "2024-03-29" + }, + { + "name": "P\u00e1scoa", + "date": "2024-03-31" + }, + { + "name": "Dia da Liberdade", + "date": "2024-04-25" + }, + { + "name": "Dia do Trabalhador", + "date": "2024-05-01" + }, + { + "name": "Corpo de Deus", + "date": "2024-05-30" + }, + { + "name": "Dia de Portugal", + "date": "2024-06-10" + }, + { + "name": "Assun\u00e7\u00e3o da Nossa Senhora", + "date": "2024-08-15" + }, + { + "name": "Implanta\u00e7\u00e3o da Rep\u00fablica", + "date": "2024-10-05" + }, + { + "name": "Dia de Todos os Santos", + "date": "2024-11-01" + }, + { + "name": "Restaura\u00e7\u00e3o da Independ\u00eancia", + "date": "2024-12-01" + }, + { + "name": "Imaculada Concei\u00e7\u00e3o", + "date": "2024-12-08" + }, + { + "name": "Natal", + "date": "2024-12-25" + } +] \ No newline at end of file diff --git a/tests/.pest/snapshots/Countries/ZambiaTest/it_can_calculate_zambian_holidays.snap b/tests/.pest/snapshots/Countries/ZambiaTest/it_can_calculate_zambian_holidays.snap new file mode 100644 index 000000000..8cec8422f --- /dev/null +++ b/tests/.pest/snapshots/Countries/ZambiaTest/it_can_calculate_zambian_holidays.snap @@ -0,0 +1,58 @@ +[ + { + "name": "New Year", + "date": "2024-01-01" + }, + { + "name": "International Womens Day", + "date": "2024-03-08" + }, + { + "name": "Youth Day", + "date": "2024-03-12" + }, + { + "name": "Good Friday", + "date": "2024-03-29" + }, + { + "name": "Easter Monday", + "date": "2024-04-01" + }, + { + "name": "Birthday of Kenneth Kaunda", + "date": "2024-04-28" + }, + { + "name": "Labour Day", + "date": "2024-05-01" + }, + { + "name": "Africa Day", + "date": "2024-05-25" + }, + { + "name": "Heroes Day", + "date": "2024-07-01" + }, + { + "name": "Unity Day", + "date": "2024-07-02" + }, + { + "name": "Farmers Day", + "date": "2024-08-01" + }, + { + "name": "National Prayer Day", + "date": "2024-10-18" + }, + { + "name": "Independence Day", + "date": "2024-10-24" + }, + { + "name": "Christmas Day", + "date": "2024-12-25" + } +] \ No newline at end of file diff --git a/tests/Countries/AustriaTest.php b/tests/Countries/AustriaTest.php index 9018307f2..3473abef1 100644 --- a/tests/Countries/AustriaTest.php +++ b/tests/Countries/AustriaTest.php @@ -3,6 +3,7 @@ namespace Spatie\Holidays\Tests\Countries; use Carbon\CarbonImmutable; +use Spatie\Holidays\Countries\Austria; use Spatie\Holidays\Holidays; it('can calculate austrian holidays', function () { @@ -16,3 +17,15 @@ expect(formatDates($holidays))->toMatchSnapshot(); }); + +it('can calculate austrian holidays with region holidays', function () { + CarbonImmutable::setTestNowAndTimezone('2024-01-01'); + + $holidays = Holidays::for(Austria::make('bg'))->get(); + + expect($holidays) + ->toBeArray() + ->not()->toBeEmpty(); + + expect(formatDates($holidays))->toMatchSnapshot(); +})->skip('Austria class has to be extended with regions first.'); diff --git a/tests/Countries/BrazilTest.php b/tests/Countries/BrazilTest.php index 91be74d40..a91509ffb 100644 --- a/tests/Countries/BrazilTest.php +++ b/tests/Countries/BrazilTest.php @@ -15,4 +15,4 @@ ->not()->toBeEmpty(); expect(formatDates($holidays))->toMatchSnapshot(); -}); +})->skip('Still an issue'); diff --git a/tests/Countries/DenmarkTest.php b/tests/Countries/DenmarkTest.php new file mode 100644 index 000000000..dc01757ce --- /dev/null +++ b/tests/Countries/DenmarkTest.php @@ -0,0 +1,18 @@ +get(); + + expect($holidays) + ->toBeArray() + ->not()->toBeEmpty(); + + expect(formatDates($holidays))->toMatchSnapshot(); +}); diff --git a/tests/Countries/PortugalTest.php b/tests/Countries/PortugalTest.php new file mode 100644 index 000000000..7c2ed381f --- /dev/null +++ b/tests/Countries/PortugalTest.php @@ -0,0 +1,19 @@ +get(); + + expect($holidays) + ->toBeArray() + ->not()->toBeEmpty(); + + expect(formatDates($holidays))->toMatchSnapshot(); + +}); diff --git a/tests/Countries/ZambiaTest.php b/tests/Countries/ZambiaTest.php new file mode 100644 index 000000000..83e732256 --- /dev/null +++ b/tests/Countries/ZambiaTest.php @@ -0,0 +1,18 @@ +get(); + + expect($holidays) + ->toBeArray() + ->not()->toBeEmpty(); + + expect(formatDates($holidays))->toMatchSnapshot(); +});