diff --git a/src/Countries/NewZealand.php b/src/Countries/NewZealand.php new file mode 100644 index 000000000..d1aba1f56 --- /dev/null +++ b/src/Countries/NewZealand.php @@ -0,0 +1,137 @@ +observedHolidays($year), + $this->variableHolidays($year), + ); + } + + protected function observedHolidays(int $year): array + { + //https://www.employment.govt.nz/leave-and-holidays/public-holidays/public-holidays-and-anniversary-dates/ + $holidays = [ + 'New Year\'s Day' => '01-01', + 'Day after New Year\'s Day' => '01-02', + 'Waitangi Day' => '02-06', + 'ANZAC Day' => '04-25', + 'Christmas Day' => '12-25', + 'Boxing Day' => '12-26', + ]; + + //https://www.employment.govt.nz/leave-and-holidays/public-holidays/public-holidays-falling-on-a-weekend/ + foreach ($holidays as $name => $date) { + $observedDay = match ($name) { + 'Day after New Year\'s Day' => $this->secondOfJanuary($year), + 'Christmas Day' => $this->observedChristmasDay($year), + 'Boxing Day' => $this->observedBoxingDay($year), + default => $this->weekendToNextMonday($date, $year), + }; + + if ($observedDay) { + $holidays[$name.' (Mondayisation)'] = $observedDay; + unset($holidays[$name]); + } + } + + return $holidays; + } + + protected function variableHolidays(int $year): array + { + //Easter + $easterSunday = $this->easter($year); + $goodFriday = $easterSunday->subDays(2); + $easterMonday = $easterSunday->addDay(); + + //Sovereign Birthday + $sovereignTitle = $this->sovereignBirthdayKey($year); + $sovereignMonday = CarbonImmutable::parse("first monday of june {$year}"); + + //Labour Day + $labourMonday = CarbonImmutable::parse("fourth monday of october {$year}"); + + return [ + 'Good Friday' => $goodFriday, + 'Easter Monday' => $easterMonday, + $sovereignTitle => $sovereignMonday, + 'Matariki' => $this->calculateMatariki($year), + 'Labour Day' => $labourMonday, + ]; + } + + protected function secondOfJanuary(int $year): ?CarbonInterface + { + $newYearsDay = (new CarbonImmutable($year.'-01-01'))->startOfDay(); + $secondOfJanuary = $newYearsDay->addDay(); + + return match ($newYearsDay->dayName) { + 'Friday' => $secondOfJanuary->next('monday'), + 'Saturday', 'Sunday' => $secondOfJanuary->next('tuesday'), + default => null, + }; + } + + protected function sovereignBirthdayKey(int $year): string + { + return $year >= 2023 ? "King's Birthday" : "Queen's Birthday"; + } + + private function calculateMatariki(int $year): ?CarbonImmutable + { + //https://www.tepapa.govt.nz/discover-collections/read-watch-play/matariki-maori-new-year/dates-for-matariki-public-holiday + $matarikiDates = [ + 2022 => '2022-06-24', + 2023 => '2023-07-14', + 2024 => '2024-06-28', + 2025 => '2025-06-20', + 2026 => '2026-07-10', + 2027 => '2027-06-25', + 2028 => '2028-07-14', + 2029 => '2029-07-06', + 2030 => '2030-06-21', + 2031 => '2031-07-11', + 2032 => '2032-07-02', + 2033 => '2033-06-24', + 2034 => '2034-07-07', + 2035 => '2035-06-29', + 2036 => '2036-07-18', + 2037 => '2037-07-10', + 2038 => '2038-06-25', + 2039 => '2039-07-15', + 2040 => '2040-07-06', + 2041 => '2041-07-19', + 2042 => '2042-07-11', + 2043 => '2043-07-03', + 2044 => '2044-06-24', + 2045 => '2045-07-07', + 2046 => '2046-06-29', + 2047 => '2047-07-19', + 2048 => '2048-07-03', + 2049 => '2049-06-25', + 2050 => '2050-07-15', + 2051 => '2051-06-30', + 2052 => '2052-06-21', + ]; + + return isset($matarikiDates[$year]) + ? CarbonImmutable::createFromFormat('Y-m-d', $matarikiDates[$year])->setTimezone('Pacific/Auckland') + : null; // Return null if year not defined + } +} diff --git a/tests/.pest/snapshots/Countries/NewZealandTest/it_can_calculate_holidays_for_2030.snap b/tests/.pest/snapshots/Countries/NewZealandTest/it_can_calculate_holidays_for_2030.snap new file mode 100644 index 000000000..a74f80b6f --- /dev/null +++ b/tests/.pest/snapshots/Countries/NewZealandTest/it_can_calculate_holidays_for_2030.snap @@ -0,0 +1,46 @@ +[ + { + "name": "New Year's Day", + "date": "2030-01-01" + }, + { + "name": "Day after New Year's Day", + "date": "2030-01-02" + }, + { + "name": "Waitangi Day", + "date": "2030-02-06" + }, + { + "name": "Good Friday", + "date": "2030-04-19" + }, + { + "name": "Easter Monday", + "date": "2030-04-22" + }, + { + "name": "ANZAC Day", + "date": "2030-04-25" + }, + { + "name": "King's Birthday", + "date": "2030-06-03" + }, + { + "name": "Matariki", + "date": "2030-06-21" + }, + { + "name": "Labour Day", + "date": "2030-10-28" + }, + { + "name": "Christmas Day", + "date": "2030-12-25" + }, + { + "name": "Boxing Day", + "date": "2030-12-26" + } +] \ No newline at end of file diff --git a/tests/.pest/snapshots/Countries/NewZealandTest/it_can_calculate_new_zealand_holidays.snap b/tests/.pest/snapshots/Countries/NewZealandTest/it_can_calculate_new_zealand_holidays.snap new file mode 100644 index 000000000..60cdbbf06 --- /dev/null +++ b/tests/.pest/snapshots/Countries/NewZealandTest/it_can_calculate_new_zealand_holidays.snap @@ -0,0 +1,46 @@ +[ + { + "name": "New Year's Day", + "date": "2024-01-01" + }, + { + "name": "Day after New Year's Day", + "date": "2024-01-02" + }, + { + "name": "Waitangi Day", + "date": "2024-02-06" + }, + { + "name": "Good Friday", + "date": "2024-03-29" + }, + { + "name": "Easter Monday", + "date": "2024-04-01" + }, + { + "name": "ANZAC Day", + "date": "2024-04-25" + }, + { + "name": "King's Birthday", + "date": "2024-06-03" + }, + { + "name": "Matariki", + "date": "2024-06-28" + }, + { + "name": "Labour Day", + "date": "2024-10-28" + }, + { + "name": "Christmas Day", + "date": "2024-12-25" + }, + { + "name": "Boxing Day", + "date": "2024-12-26" + } +] \ No newline at end of file diff --git a/tests/.pest/snapshots/Countries/NewZealandTest/it_can_calculate_new_zealand_holidays_if_christmas_is_on_a_friday.snap b/tests/.pest/snapshots/Countries/NewZealandTest/it_can_calculate_new_zealand_holidays_if_christmas_is_on_a_friday.snap new file mode 100644 index 000000000..5f1a5d618 --- /dev/null +++ b/tests/.pest/snapshots/Countries/NewZealandTest/it_can_calculate_new_zealand_holidays_if_christmas_is_on_a_friday.snap @@ -0,0 +1,46 @@ +[ + { + "name": "New Year's Day (Mondayisation)", + "date": "2028-01-03" + }, + { + "name": "Day after New Year's Day (Mondayisation)", + "date": "2028-01-04" + }, + { + "name": "Waitangi Day (Mondayisation)", + "date": "2028-02-07" + }, + { + "name": "Good Friday", + "date": "2028-04-14" + }, + { + "name": "Easter Monday", + "date": "2028-04-17" + }, + { + "name": "ANZAC Day", + "date": "2028-04-25" + }, + { + "name": "King's Birthday", + "date": "2028-06-05" + }, + { + "name": "Matariki", + "date": "2028-07-14" + }, + { + "name": "Labour Day", + "date": "2028-10-23" + }, + { + "name": "Christmas Day", + "date": "2028-12-25" + }, + { + "name": "Boxing Day", + "date": "2028-12-26" + } +] \ No newline at end of file diff --git a/tests/.pest/snapshots/Countries/NewZealandTest/it_can_calculate_new_zealand_holidays_if_christmas_is_on_a_saturday.snap b/tests/.pest/snapshots/Countries/NewZealandTest/it_can_calculate_new_zealand_holidays_if_christmas_is_on_a_saturday.snap new file mode 100644 index 000000000..b58d9b904 --- /dev/null +++ b/tests/.pest/snapshots/Countries/NewZealandTest/it_can_calculate_new_zealand_holidays_if_christmas_is_on_a_saturday.snap @@ -0,0 +1,46 @@ +[ + { + "name": "New Year's Day", + "date": "2027-01-01" + }, + { + "name": "Day after New Year's Day (Mondayisation)", + "date": "2027-01-04" + }, + { + "name": "Waitangi Day (Mondayisation)", + "date": "2027-02-08" + }, + { + "name": "Good Friday", + "date": "2027-03-26" + }, + { + "name": "Easter Monday", + "date": "2027-03-29" + }, + { + "name": "ANZAC Day (Mondayisation)", + "date": "2027-04-26" + }, + { + "name": "King's Birthday", + "date": "2027-06-07" + }, + { + "name": "Matariki", + "date": "2027-06-25" + }, + { + "name": "Labour Day", + "date": "2027-10-25" + }, + { + "name": "Christmas Day (Mondayisation)", + "date": "2027-12-27" + }, + { + "name": "Boxing Day (Mondayisation)", + "date": "2027-12-28" + } +] \ No newline at end of file diff --git a/tests/.pest/snapshots/Countries/NewZealandTest/it_can_calculate_new_zealand_holidays_if_christmas_is_on_a_sunday.snap b/tests/.pest/snapshots/Countries/NewZealandTest/it_can_calculate_new_zealand_holidays_if_christmas_is_on_a_sunday.snap new file mode 100644 index 000000000..56b347e46 --- /dev/null +++ b/tests/.pest/snapshots/Countries/NewZealandTest/it_can_calculate_new_zealand_holidays_if_christmas_is_on_a_sunday.snap @@ -0,0 +1,46 @@ +[ + { + "name": "New Year's Day (Mondayisation)", + "date": "2022-01-03" + }, + { + "name": "Day after New Year's Day (Mondayisation)", + "date": "2022-01-04" + }, + { + "name": "Waitangi Day (Mondayisation)", + "date": "2022-02-07" + }, + { + "name": "Good Friday", + "date": "2022-04-15" + }, + { + "name": "Easter Monday", + "date": "2022-04-18" + }, + { + "name": "ANZAC Day", + "date": "2022-04-25" + }, + { + "name": "Queen's Birthday", + "date": "2022-06-06" + }, + { + "name": "Matariki", + "date": "2022-06-24" + }, + { + "name": "Labour Day", + "date": "2022-10-24" + }, + { + "name": "Boxing Day", + "date": "2022-12-26" + }, + { + "name": "Christmas Day (Mondayisation)", + "date": "2022-12-27" + } +] \ No newline at end of file diff --git a/tests/.pest/snapshots/Countries/NewZealandTest/it_returns_a_substitute_day_if_new_years_day_falls_on_a_weekend.snap b/tests/.pest/snapshots/Countries/NewZealandTest/it_returns_a_substitute_day_if_new_years_day_falls_on_a_weekend.snap new file mode 100644 index 000000000..6f12b3993 --- /dev/null +++ b/tests/.pest/snapshots/Countries/NewZealandTest/it_returns_a_substitute_day_if_new_years_day_falls_on_a_weekend.snap @@ -0,0 +1,46 @@ +[ + { + "name": "New Year's Day (Mondayisation)", + "date": "2033-01-03" + }, + { + "name": "Day after New Year's Day (Mondayisation)", + "date": "2033-01-04" + }, + { + "name": "Waitangi Day (Mondayisation)", + "date": "2033-02-07" + }, + { + "name": "Good Friday", + "date": "2033-04-15" + }, + { + "name": "Easter Monday", + "date": "2033-04-18" + }, + { + "name": "ANZAC Day", + "date": "2033-04-25" + }, + { + "name": "King's Birthday", + "date": "2033-06-06" + }, + { + "name": "Matariki", + "date": "2033-06-24" + }, + { + "name": "Labour Day", + "date": "2033-10-24" + }, + { + "name": "Boxing Day", + "date": "2033-12-26" + }, + { + "name": "Christmas Day (Mondayisation)", + "date": "2033-12-27" + } +] \ No newline at end of file diff --git a/tests/Countries/NewZealandTest.php b/tests/Countries/NewZealandTest.php new file mode 100644 index 000000000..056d7b467 --- /dev/null +++ b/tests/Countries/NewZealandTest.php @@ -0,0 +1,137 @@ +getName($testDate); + $isHoliday = Holidays::for('nz')->isHoliday($testDate); + + expect($holidayName)->toBe($name)->and($isHoliday)->toBeTrue(); + +})->with([ + //2023 + ['New Year\'s Day (Mondayisation)', '2023-01-02'], + ['Day after New Year\'s Day (Mondayisation)', '2023-01-03'], + ['Waitangi Day', '2023-02-06'], + ['Good Friday', '2023-04-07'], + ['Easter Monday', '2023-04-10'], + ['ANZAC Day', '2023-04-25'], + ['King\'s Birthday', '2023-06-05'], + ['Matariki', '2023-07-14'], + ['Labour Day', '2023-10-23'], + ['Christmas Day', '2023-12-25'], + ['Boxing Day', '2023-12-26'], + //2024 + ['New Year\'s Day', '2024-01-01'], + ['Day after New Year\'s Day', '2024-01-02'], + ['Waitangi Day', '2024-02-06'], + ['Good Friday', '2024-03-29'], + ['Easter Monday', '2024-04-01'], + ['ANZAC Day', '2024-04-25'], + ['King\'s Birthday', '2024-06-03'], + ['Matariki', '2024-06-28'], + ['Labour Day', '2024-10-28'], + ['Christmas Day', '2024-12-25'], + ['Boxing Day', '2024-12-26'], + //2025 + ['New Year\'s Day', '2025-01-01'], + ['Day after New Year\'s Day', '2025-01-02'], + ['Waitangi Day', '2025-02-06'], + ['Good Friday', '2025-04-18'], + ['Easter Monday', '2025-04-21'], + ['ANZAC Day', '2025-04-25'], + ['King\'s Birthday', '2025-06-02'], + ['Matariki', '2025-06-20'], + ['Labour Day', '2025-10-27'], + ['Christmas Day', '2025-12-25'], + ['Boxing Day', '2025-12-26'], + //2027 - Lots of Mondayisation + ['New Year\'s Day', '2027-01-01'], + ['Day after New Year\'s Day (Mondayisation)', '2027-01-04'], + ['Waitangi Day (Mondayisation)', '2027-02-08'], + ['Good Friday', '2027-03-26'], + ['Easter Monday', '2027-03-29'], + ['ANZAC Day (Mondayisation)', '2027-04-26'], + ['King\'s Birthday', '2027-06-07'], + ['Matariki', '2027-06-25'], + ['Labour Day', '2027-10-25'], + ['Christmas Day (Mondayisation)', '2027-12-27'], + ['Boxing Day (Mondayisation)', '2027-12-28'], +]); + +it('calculates new zealand easter holidays by date', function (string $name, string $date) { + + $testDate = CarbonImmutable::parse($date); + + $holidayName = Holidays::for('nz')->getName($testDate); + $isHoliday = Holidays::for('nz')->isHoliday($testDate); + + expect($holidayName)->toBe($name)->and($isHoliday)->toBeTrue(); + +})->with([ + ['Good Friday', '2024-03-29'], + ['Easter Monday', '2024-04-01'], + ['Good Friday', '2025-04-18'], + ['Easter Monday', '2025-04-21'], + ['Good Friday', '2026-04-03'], + ['Easter Monday', '2026-04-06'], +]); + +it('can calculate new zealand holidays', function () { + CarbonImmutable::setTestNow('2024-01-01'); + + $holidays = Holidays::for(country: 'nz')->get(); + + expect($holidays)->toBeArray()->not()->toBeEmpty(); + expect(formatDates($holidays))->toMatchSnapshot(); + +}); + +it('returns a substitute day if new years day falls on a weekend', function () { + CarbonImmutable::setTestNow('2033-01-01'); + + $holidays = Holidays::for(country: 'nz')->get(); + + expect($holidays)->toBeArray()->not()->toBeEmpty(); + expect(formatDates($holidays))->toMatchSnapshot(); +}); + +it('can calculate new zealand holidays if christmas is on a friday', function () { + CarbonImmutable::setTestNow('2028-01-01'); + + $holidays = Holidays::for(country: 'nz')->get(); + + expect($holidays)->toBeArray()->not()->toBeEmpty(); + expect(formatDates($holidays))->toMatchSnapshot(); +}); + +it('can calculate new zealand holidays if christmas is on a saturday', function () { + CarbonImmutable::setTestNow('2027-01-01'); + + $holidays = Holidays::for(country: 'nz')->get(); + + expect($holidays)->toBeArray()->not()->toBeEmpty(); + expect(formatDates($holidays))->toMatchSnapshot(); +}); + +it('can calculate new zealand holidays if christmas is on a sunday', function () { + CarbonImmutable::setTestNow('2022-01-01'); + + $holidays = Holidays::for(country: 'nz')->get(); + + expect($holidays)->toBeArray()->not()->toBeEmpty(); + expect(formatDates($holidays))->toMatchSnapshot(); +}); + +it('can calculate holidays for 2030', function () { + CarbonImmutable::setTestNow('2030-01-01'); + + $holidays = Holidays::for(country: 'nz')->get(); + + expect($holidays)->toBeArray()->not()->toBeEmpty(); + expect(formatDates($holidays))->toMatchSnapshot(); +});