Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Update Bangladesh holiday calculations #249

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 24 additions & 9 deletions lang/bangladesh/bn/holidays.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,25 @@
{
"International Mother Language Day": "Antorjatik Matribhasha Dibos",
"Birthday of Sheikh Mujibur Rahman": "Shekh Mujibur Rahmaner Jonmodin",
"Independence Day": "Swadhinota Dibos",
"Bengali New Year": "Bangla Noboborsho",
"May Day": "Antorjatik Shrom Dibos",
"National Mourning Day": "Jatiyo Shok Dibos",
"Victory Day": "Bijoy Dibos",
"Christmas Day": "Borodin"
}
"International Mother Language Day": "শহীদ দিবস",
"Independence Day": "স্বাধীনতা দিবস",
"May Day": "মে দিবস",
"National Mourning Day": "জাতীয় শোক দিবস",
"Victory Day": "বিজয় দিবস",
"Christmas Day": "বড়দিন",
"Shab-e-Barat": "শবে বরাত",
"Shab-e-Qadr": "শবে কদর",
"Shab-e-Meraj": "শবে মেরাজ",
"Eid ul-Fitr": "ঈদ-উল-ফিতর",
"Eid ul-Fitr Eve": "ঈদ-উল-ফিতরের আগের দিন",
"Eid ul-Fitr Holiday": "ঈদ-উল-ফিতরের ছুটি",
"Eid ul-Adha": "ঈদ-উল-আযহা",
"Eid ul-Adha Eve": "ঈদ-উল-আযহার আগের দিন",
"Eid ul-Adha Holiday": "ঈদ-উল-আযহার ছুটি",
"Ashura": "আশুরা",
"Eid-e-Miladunnabi": "ঈদে মিলাদুন্নবী",
"Pohela Boishakh": "পহেলা বৈশাখ",
"Buddha Purnima": "বুদ্ধ পূর্ণিমা",
"Jonmashtomi": "জন্মাষ্টমী",
"Durga Puja": "দুর্গা পূজা",
"Birthday of Sheikh Mujibur Rahman": "শেখ মুজিবুর রহমানের জন্মদিন",
"Bengali New Year": "বাংলা নববর্ষ"
}
261 changes: 261 additions & 0 deletions src/Calendars/BangladeshiCalander.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
<?php

namespace Spatie\Holidays\Calendars;

use Carbon\CarbonImmutable;
use Carbon\CarbonPeriod;
use Spatie\Holidays\Countries\Country;

/**
* Handles holiday calculations for Bangladesh including:
* - Islamic holidays (using Hijri calendar)
* - Hindu festivals (using lunar calendar)
* - Buddhist festivals (using lunar calendar)
* - National holidays (using Gregorian calendar)
*
* @mixin Country
* @property string $islamicCalendarTimezone
*/
trait BangladeshiCalander
{
use IslamicCalendar {
IslamicCalendar::setIslamicCalendarTimezone as private parentSetIslamicCalendarTimezone;
}

/*
|--------------------------------------------------------------------------
| Islamic Holiday Calculations
|--------------------------------------------------------------------------
*/

/** @return array<CarbonPeriod> */
protected function shabEBarat(int $year): array
{
return $this->getIslamicHoliday(
month: self::ISLAMIC_MONTHS['SHABAN'],
day: '15',
year: $year
);
}

/** @return array<CarbonPeriod> */
protected function shabEQadr(int $year): array
{
return $this->getIslamicHoliday(
month: self::ISLAMIC_MONTHS['RAMADAN'],
day: '27',
year: $year
);
}

/** @return array<CarbonPeriod> */
protected function eidUlFitr(int $year): array
{
return $this->getIslamicHoliday(
month: self::ISLAMIC_MONTHS['SHAWWAL'],
day: '01',
year: $year,
daysBeforeAfter: [2, 2] // 2 days before and 2 days after
);
}

/** @return array<CarbonPeriod> */
protected function eidUlAdha(int $year): array
{
return $this->getIslamicHoliday(
month: self::ISLAMIC_MONTHS['DHU_AL_HIJJAH'],
day: '10',
year: $year,
daysBeforeAfter: [2, 3] // 2 days before and 3 days after
);
}

/** @return array<CarbonPeriod> */
protected function eideMiladunnabi(int $year): array
{
return $this->getIslamicHoliday(
month: self::ISLAMIC_MONTHS['RABI_AL_AWWAL'],
day: '12',
year: $year
);
}

/** @return array<CarbonPeriod> */
protected function ashura(int $year): array
{
return $this->getIslamicHoliday(
month: self::ISLAMIC_MONTHS['MUHARRAM'],
day: '10',
year: $year
);
}

/** @return array<CarbonPeriod> */
protected function shabEMeraj(int $year): array
{
return $this->getIslamicHoliday(
month: self::ISLAMIC_MONTHS['RAJAB'],
day: '27',
year: $year
);
}

/*
|--------------------------------------------------------------------------
| Bengali Calendar Holiday Calculations
|--------------------------------------------------------------------------
*/

/** @return array<CarbonPeriod> */
protected function pohela_boishakh(int $year): array
{
// Bengali New Year is typically April 14
// Handles leap year adjustments
$date = CarbonImmutable::create($year, 4, 14);
if ($date->isLeapYear() && $date->month > 2) {
$date = $date->addDay();
}

return [$this->createPeriod($date->format('m-d'), $year, 1)];
}

/*
|--------------------------------------------------------------------------
| Hindu and Buddhist Holiday Calculations
|--------------------------------------------------------------------------
*/

/** @return array<CarbonPeriod> */
protected function buddhaPurnima(int $year): array
{
// Calculate first full moon day of May
$baseDate = CarbonImmutable::create($year, 5, 1);
$fullMoonDate = $this->findNearestFullMoon($baseDate);

return [$this->createPeriod($fullMoonDate->format('m-d'), $year, 1)];
}

/** @return array<CarbonPeriod> */
protected function jonmashtomi(int $year): array
{
// Krishna Janmashtami falls on the eighth day of the dark fortnight
$baseDate = CarbonImmutable::create($year, 8, 15);
$ashtamiDate = $this->calculateAshtamiDate($baseDate);

return [$this->createPeriod($ashtamiDate->format('m-d'), $year, 1)];
}

/** @return array<CarbonPeriod> */
protected function durgaPuja(int $year): array
{
// Durga Puja's main days (Ashtami and Navami)
$baseDate = CarbonImmutable::create($year, 10, 15);
$ashtamiDate = $this->calculateAshtamiDate($baseDate);

return [$this->createPeriod($ashtamiDate->format('m-d'), $year, 2)];
}

/*
|--------------------------------------------------------------------------
| Helper Methods
|--------------------------------------------------------------------------
*/

/**
* Helper method to calculate Islamic holiday dates
*
* @param string $month Islamic month number
* @param string $day Day of the month
* @param int $year Gregorian year
* @param array<int> $daysBeforeAfter [daysBefore, daysAfter]
* @return array<CarbonPeriod>
*/
private function getIslamicHoliday(
string $month,
string $day,
int $year,
array $daysBeforeAfter = [0, 0]
): array {
$date = $this->islamicToGregorianDate("$month-$day", $year);

if (!empty($daysBeforeAfter)) {
$date = $date->subDays($daysBeforeAfter[0]);
$totalDays = 1 + $daysBeforeAfter[0] + $daysBeforeAfter[1];
} else {
$totalDays = 1;
}

return [$this->createPeriod($date->format('m-d'), $year, $totalDays)];
}

/**
* Calculate lunar phase (0 = new moon, 0.5 = full moon)
*/
private function getLunarPhase(CarbonImmutable $date): float
{
$year = $date->year;
$month = $date->month;
$day = $date->day;

$c = $date->getPreciseTimestamp() / 86400;
$e = $c - 2451550.1;
$phase = $e / 29.530588853;

return $phase - floor($phase);
}

/**
* Find the nearest full moon to a given date
*/
private function findNearestFullMoon(CarbonImmutable $targetDate): CarbonImmutable
{
$date = $targetDate->copy();
$closestDate = $date;
$closestDiff = 1;

// Check 15 days before and after
for ($i = -15; $i <= 15; $i++) {
$checkDate = $date->addDays($i);
$phase = $this->getLunarPhase($checkDate);
$diff = abs($phase - 0.5);

if ($diff < $closestDiff) {
$closestDiff = $diff;
$closestDate = $checkDate;
}
}

return $closestDate;
}

/**
* Calculate Ashtami (eighth day) date for Hindu festivals
*/
private function calculateAshtamiDate(CarbonImmutable $baseDate): CarbonImmutable
{
$newMoon = $this->findNearestNewMoon($baseDate);
return $newMoon->addDays(8);
}

/**
* Find the nearest new moon to a given date
*/
private function findNearestNewMoon(CarbonImmutable $date): CarbonImmutable
{
$closestDate = $date;
$closestDiff = 1;

for ($i = -15; $i <= 15; $i++) {
$checkDate = $date->addDays($i);
$phase = $this->getLunarPhase($checkDate);
$diff = min($phase, 1 - $phase);

if ($diff < $closestDiff) {
$closestDiff = $diff;
$closestDate = $checkDate;
}
}

return $closestDate;
}
}
Loading