diff --git a/README.md b/README.md index 727bac2ed..7aa2ae0de 100644 --- a/README.md +++ b/README.md @@ -68,11 +68,19 @@ use Spatie\Holidays\Holidays; $holidays = Holidays::for(country: 'be', year: 2024))->get(); ``` -### Getting holidays in a specific language +### Getting holidays between two dates + +You can also get all holidays between two dates (inclusive). ```php use Spatie\Holidays\Holidays; +$holidays = Holidays::for('be')->getInRange('2023-06-01', '2024-05-31'); +``` + +### Getting holidays in a specific language + +```php $holidays = Holidays::for(country: 'be', locale: 'fr'))->get(); ``` diff --git a/src/Countries/Country.php b/src/Countries/Country.php index c2a5b6276..ea0f20b7b 100644 --- a/src/Countries/Country.php +++ b/src/Countries/Country.php @@ -52,6 +52,41 @@ public function get(int $year, ?string $locale = null): array return $translatedHolidays; } + /** @return array date => name */ + public function getInRange(?CarbonImmutable $from, ?CarbonImmutable $to): array + { + $from ??= CarbonImmutable::now()->startOfYear(); + $to ??= CarbonImmutable::now()->endOfYear(); + + $this->ensureYearCanBeCalculated($from->year); + $this->ensureYearCanBeCalculated($to->year); + + $allHolidays = []; + + for ($year = $from->year; $year <= $to->year; $year++) { + $yearHolidays = $this->get($year); + /** + * @var string $name + * @var CarbonImmutable $date + */ + foreach ($yearHolidays as $name => $date) { + if ($date->between($from, $to)) { + $allHolidays[] = ['date' => $date, 'name' => $name]; + } + } + } + + usort($allHolidays, static fn (array $a, array $b) => $a['date'] <=> $b['date']); + + $mappedHolidays = []; + /** @var array{date: CarbonImmutable, name: string} $holiday */ + foreach ($allHolidays as $holiday) { + $mappedHolidays[$holiday['date']->toDateString()] = $holiday['name']; + } + + return $mappedHolidays; + } + public static function make(): static { return new static(...func_get_args()); diff --git a/src/Holidays.php b/src/Holidays.php index 536171426..03a79aee2 100755 --- a/src/Holidays.php +++ b/src/Holidays.php @@ -15,6 +15,8 @@ protected function __construct( protected Country $country, protected int $year, protected ?string $locale = null, + protected ?CarbonImmutable $from = null, + protected ?CarbonImmutable $to = null, ) { } @@ -45,6 +47,43 @@ public function get(Country|string|null $country = null, ?int $year = null): arr ->toArray(); } + /** + * getInRange method allows you to pick holidays in a range of dates, + * - dates are inclusive. + * - dates are swappable, lower date could be passed as second argument. + * - dates could be a CarbonInterface or a string. + * - acceptable strings formats are 'Y-m-d' or 'Y-m' or 'Y' + * - if passed string is 'Y-m' or 'Y' it will be converted to first(from) / last{to} day of the month(from) / year(to) + * E.g. to retrieve all holidays in between + * - 2020-01-01 and 2024-12-31, you could use: getInRange('2020-01-01', '2024-12-31'), getInRange('2020-01', '2024-12') or getInRange('2020', '2024') + * - 2024-06-01 and 2025-05-30, you could use: getInRange('2024-06-01', '2025-05-30'), getInRange('2024-06', '2025-05') + * @return array date => name + */ + public function getInRange(CarbonInterface|string $from, CarbonInterface|string $to): array + { + if (! $from instanceof CarbonImmutable) { + $from = match (strlen($from)) { + 4 => CarbonImmutable::parse($from . '-01-01'), + 7 => CarbonImmutable::parse($from . '-01'), + default => CarbonImmutable::parse($from), + }; + } + + if (! $to instanceof CarbonImmutable) { + $to = match (strlen($to)) { + 4 => CarbonImmutable::parse($to . '-12-31'), + 7 => CarbonImmutable::parse($to)->endOfMonth(), + default => CarbonImmutable::parse($to), + }; + } + + if ($from->gt($to)) { + [$from, $to] = [$to, $from]; + } + + return $this->country->getInRange($from, $to); + } + public function isHoliday(CarbonInterface|string $date, Country|string|null $country = null): bool { if (! $date instanceof CarbonImmutable) { diff --git a/tests/HolidaysTest.php b/tests/HolidaysTest.php index 6c83275b9..db3e209f3 100644 --- a/tests/HolidaysTest.php +++ b/tests/HolidaysTest.php @@ -84,6 +84,23 @@ expect($result)->toBeNull(); }); + +it('can get all holidays between two dates', function (string|CarbonImmutable $from, string|CarbonImmutable $to, int $expectedCount, string $firstName, string $lastName) { + $holidays = Holidays::for('be')->getInRange($from, $to); + + expect($holidays)->toBeArray(); + expect($holidays)->toHaveCount($expectedCount); + expect(reset($holidays))->toBe($firstName); + expect(end($holidays))->toBe($lastName); +})->with([ + ['2020', '2024', 50, 'Nieuwjaar', 'Kerstmis'], + ['2024-06', '2025-05', 9, 'Nationale Feestdag', 'OLH Hemelvaart'], + ['2023-06-01', '2024-05-30', 10, 'Nationale Feestdag', 'Pinkstermaandag'], + ['2024-05-30', '2023-06-01', 10, 'Nationale Feestdag', 'Pinkstermaandag'], + [CarbonImmutable::parse('2023-06-01'), CarbonImmutable::parse('2024-05-30'), 10, 'Nationale Feestdag', 'Pinkstermaandag'], + [CarbonImmutable::parse('2023-06-01'), '2024-05', 10, 'Nationale Feestdag', 'Pinkstermaandag'], +]); + it('can get the country is supported', function () { $result = Holidays::has(country: 'be'); expect($result)->toBeTrue();