Skip to content

Commit

Permalink
Add Moroccan Holidays (#56)
Browse files Browse the repository at this point in the history
* add-Moroccan-holidays

* integrate hijri-dates to calculate Islamic Holidays

* Fix PHPStan failures in GitHub Actions

* Refactor: Removed dependency on geniusts/hijri-dates package and integrated the calculation function from the Ministry of Endowments and Islamic Affairs.

* Add translations for holiday names, include missing holiday, and fix unit test coverage for the new holiday addition

* Fix PHPStan issues & Code Style
  • Loading branch information
YazidKHALDI authored Jun 13, 2024
1 parent 7d60ff9 commit dce5e2a
Show file tree
Hide file tree
Showing 7 changed files with 506 additions and 0 deletions.
19 changes: 19 additions & 0 deletions lang/morocco/ar/holidays.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"New Year\\'s Day": "رأس السنة الميلادية",
"Proclamation of Independence Day": "تقديم وثيقة الاستقلال",
"Amazigh New Year (ⵉⴹ ⵏ ⵢⵉⵏⵏⴰⵢⵔ)": "رأس السنة الأمازيغية",
"Labour Day": "عيد الشغل",
"Throne Day": "عيد العرش",
"Oued Ed-Dahab Day": "استرجاع إقليم وادي الذهب",
"Revolution Day": "ثورة الملك والشعب",
"Youth Day": "عيد الشباب",
"Green March": "ذكرى المسيرة الخضراء",
"Independence Day": "عيد الاستقلال",
"Islamic New Year": "رأس السنة الهجرية",
"Birthday of the Prophet Muhammad": "عيد المولد النبوي",
"Birthday of the Prophet Muhammad 2": "تاني ايام عيد المولد النبوي",
"Eid al-Fitr": "عيد الفطر",
"Eid al-Fitr 2": "تاني ايام عيد الفطر",
"Eid al-Adha": "عيد الأضحى",
"Eid al-Adha 2": "تاني ايام عيد الأضحى"
}
153 changes: 153 additions & 0 deletions src/Countries/Morocco.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
<?php

namespace Spatie\Holidays\Countries;

use Carbon\CarbonImmutable;
use Spatie\Holidays\Concerns\Translatable;
use Spatie\Holidays\Contracts\HasTranslations;
use Spatie\Holidays\Exceptions\InvalidYear;

class Morocco extends Country implements HasTranslations
{
use Translatable;

public function countryCode(): string
{
return 'ma';
}

public function defaultLocale(): string
{
return 'en';
}

protected function allHolidays(int $year): array
{
return array_merge([
'New Year\'s Day' => '01-01',
'Proclamation of Independence Day' => '01-11',
'Amazigh New Year (ⵉⴹ ⵏ ⵢⵉⵏⵏⴰⵢⵔ)' => '01-14',
'Labour Day' => '05-01',
'Throne Day' => '07-30',
'Oued Ed-Dahab Day' => '08-14',
'Revolution Day' => '08-20',
'Youth Day' => '08-21',
'Green March' => '11-06',
'Independence Day' => '11-18',
], $this->variableHolidays($year));
}

/** @return array<string, CarbonImmutable> */
protected function variableHolidays(int $year): array
{
// Calculate the current Hijri year based on the Gregorian year
$currentHijriYear = 1444 + ($year - 2022);

/**
* The following holidays are considered public holidays in Morocco. However, their dates vary each year,
* as they are based on the Islamic Hijri (lunar) calendar. These holidays do not have a fixed date and
* occur based on the lunar calendar sequence. The order listed reflects the chronological occurrence
* of these holidays throughout the year.
*/

// Define Islamic holidays on the Hijri calendar
$islamicHolidaysOnHijri = [
'Islamic New Year' => '01-01',
'Birthday of the Prophet Muhammad' => '03-12',
'Birthday of the Prophet Muhammad 2' => '03-13',
'Eid al-Fitr' => '10-01',
'Eid al-Fitr 2' => '10-02',
'Eid al-Adha' => '12-10',
'Eid al-Adha 2' => '12-11',
];

$islamicHolidaysOnGregorian = [];
// Convert Hijri dates to Gregorian and filter based on the input year
foreach ($islamicHolidaysOnHijri as $holidayTitle => $hijriHolidayDate) {
[$hijriHolidayMonth, $hijriHolidayDay] = explode('-', $hijriHolidayDate);
$vlideYear = null;

$GregorianDate = $this->islamicToGregorian($currentHijriYear, (int) $hijriHolidayMonth, (int) $hijriHolidayDay);
$vlideYear = $GregorianDate['year'];
$tempCurrentHijriYear = $currentHijriYear;
while ($vlideYear != $year) {
// Convert the current Hijri holiday to Gregorian
$GregorianDate = $this->islamicToGregorian($tempCurrentHijriYear--, (int) $hijriHolidayMonth, (int) $hijriHolidayDay);
$vlideYear = $GregorianDate['year'];
if ($vlideYear < 1976) {
throw InvalidYear::yearTooLow(1976);
}
}
// Store the Gregorian date of the Islamic holiday
$islamicHolidaysOnGregorian[$holidayTitle] = CarbonImmutable::createFromFormat('Y-m-d', sprintf('%s-%s-%s', $GregorianDate['year'], $GregorianDate['month'], $GregorianDate['day']));
}

return $islamicHolidaysOnGregorian;

Check failure on line 85 in src/Countries/Morocco.php

View workflow job for this annotation

GitHub Actions / phpstan

Method Spatie\Holidays\Countries\Morocco::variableHolidays() should return array<string, Carbon\CarbonImmutable> but returns array<string, Carbon\CarbonImmutable|null>.
}

/**
* Converts a Hijri date to the corresponding Gregorian date.
* This function is adapted from the conversion tool used on the Moroccan
* Minister of Endowments and Islamic Affairs official website.
* https://www.habous.gov.ma/محول-التاريخ
*
* @param int $y The Hijri year.
* @param int $m The Hijri month.
* @param int $d The Hijri day.
* @return array{year: int, month: int, day: int} An array containing the corresponding Gregorian date in the format ['year' => YYYY, 'month' => MM, 'day' => DD].
*/
private function islamicToGregorian(int $y, int $m, int $d): array
{
$delta = 0;
$jd = $this->intPart((11 * $y + 3) / 30) + 354 * $y + 30 * $m - $this->intPart(($m - 1) / 2) + $d + 1948440 - 385 + $delta;
if ($jd > 2299160) {
$l = $jd + 68569;
$n = $this->intPart((4 * $l) / 146097);
$l = $l - $this->intPart((146097 * $n + 3) / 4);
$i = $this->intPart((4000 * ($l + 1)) / 1461001);
$l = $l - $this->intPart((1461 * $i) / 4) + 31;
$j = $this->intPart((80 * $l) / 2447);
$d = $l - $this->intPart((2447 * $j) / 80);
$l = $this->intPart($j / 11);
$m = $j + 2 - 12 * $l;
$y = 100 * ($n - 49) + $i + $l;
} else {
$j = $jd + 1402;
$k = $this->intPart(($j - 1) / 1461);
$l = $j - 1461 * $k;
$n = $this->intPart(($l - 1) / 365) - $this->intPart($l / 1461);
$i = $l - 365 * $n + 30;
$j = $this->intPart((80 * $i) / 2447);
$d = $i - $this->intPart((2447 * $j) / 80);
$i = $this->intPart($j / 11);
$m = $j + 2 - 12 * $i;
$y = 4 * $k + $n + $i - 4716;
}

return [
'year' => (int) $y,
'month' => (int) $m,
'day' => (int) $d,
];
}

/**
* Rounds a floating-point number to the nearest integer.
* If the floating-point number is negative, it uses ceil function.
* If the floating-point number is positive, it uses floor function.
*
* @param float $floatNum The floating-point number to be rounded.
* @return float The rounded integer value.
*/
private function intPart($floatNum)
{
// Check if the floating-point number is negative
if ($floatNum < -0.0000001) {
// If negative, round up using ceil
return ceil($floatNum - 0.0000001);
}

// If positive or zero, round down using floor
return floor($floatNum + 0.0000001);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
[
{
"name": "New Year's Day",
"date": "2023-01-01"
},
{
"name": "Proclamation of Independence Day",
"date": "2023-01-11"
},
{
"name": "Amazigh New Year (\u2d49\u2d39 \u2d4f \u2d62\u2d49\u2d4f\u2d4f\u2d30\u2d62\u2d54)",
"date": "2023-01-14"
},
{
"name": "Eid al-Fitr",
"date": "2023-04-22"
},
{
"name": "Eid al-Fitr 2",
"date": "2023-04-23"
},
{
"name": "Labour Day",
"date": "2023-05-01"
},
{
"name": "Eid al-Adha",
"date": "2023-06-29"
},
{
"name": "Eid al-Adha 2",
"date": "2023-06-30"
},
{
"name": "Islamic New Year",
"date": "2023-07-19"
},
{
"name": "Throne Day",
"date": "2023-07-30"
},
{
"name": "Oued Ed-Dahab Day",
"date": "2023-08-14"
},
{
"name": "Revolution Day",
"date": "2023-08-20"
},
{
"name": "Youth Day",
"date": "2023-08-21"
},
{
"name": "Birthday of the Prophet Muhammad",
"date": "2023-09-27"
},
{
"name": "Birthday of the Prophet Muhammad 2",
"date": "2023-09-28"
},
{
"name": "Green March",
"date": "2023-11-06"
},
{
"name": "Independence Day",
"date": "2023-11-18"
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
[
{
"name": "New Year's Day",
"date": "2024-01-01"
},
{
"name": "Proclamation of Independence Day",
"date": "2024-01-11"
},
{
"name": "Amazigh New Year (\u2d49\u2d39 \u2d4f \u2d62\u2d49\u2d4f\u2d4f\u2d30\u2d62\u2d54)",
"date": "2024-01-14"
},
{
"name": "Eid al-Fitr",
"date": "2024-04-10"
},
{
"name": "Eid al-Fitr 2",
"date": "2024-04-11"
},
{
"name": "Labour Day",
"date": "2024-05-01"
},
{
"name": "Eid al-Adha",
"date": "2024-06-17"
},
{
"name": "Eid al-Adha 2",
"date": "2024-06-18"
},
{
"name": "Islamic New Year",
"date": "2024-07-08"
},
{
"name": "Throne Day",
"date": "2024-07-30"
},
{
"name": "Oued Ed-Dahab Day",
"date": "2024-08-14"
},
{
"name": "Revolution Day",
"date": "2024-08-20"
},
{
"name": "Youth Day",
"date": "2024-08-21"
},
{
"name": "Birthday of the Prophet Muhammad",
"date": "2024-09-16"
},
{
"name": "Birthday of the Prophet Muhammad 2",
"date": "2024-09-17"
},
{
"name": "Green March",
"date": "2024-11-06"
},
{
"name": "Independence Day",
"date": "2024-11-18"
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
[
{
"name": "New Year's Day",
"date": "2025-01-01"
},
{
"name": "Proclamation of Independence Day",
"date": "2025-01-11"
},
{
"name": "Amazigh New Year (\u2d49\u2d39 \u2d4f \u2d62\u2d49\u2d4f\u2d4f\u2d30\u2d62\u2d54)",
"date": "2025-01-14"
},
{
"name": "Eid al-Fitr",
"date": "2025-03-31"
},
{
"name": "Eid al-Fitr 2",
"date": "2025-04-01"
},
{
"name": "Labour Day",
"date": "2025-05-01"
},
{
"name": "Eid al-Adha",
"date": "2025-06-07"
},
{
"name": "Eid al-Adha 2",
"date": "2025-06-08"
},
{
"name": "Islamic New Year",
"date": "2025-06-27"
},
{
"name": "Throne Day",
"date": "2025-07-30"
},
{
"name": "Oued Ed-Dahab Day",
"date": "2025-08-14"
},
{
"name": "Revolution Day",
"date": "2025-08-20"
},
{
"name": "Youth Day",
"date": "2025-08-21"
},
{
"name": "Birthday of the Prophet Muhammad",
"date": "2025-09-05"
},
{
"name": "Birthday of the Prophet Muhammad 2",
"date": "2025-09-06"
},
{
"name": "Green March",
"date": "2025-11-06"
},
{
"name": "Independence Day",
"date": "2025-11-18"
}
]
Loading

0 comments on commit dce5e2a

Please sign in to comment.