-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,4 @@ | ||
/.build | ||
/.idea | ||
/vendor | ||
/composer.lock | ||
/.idea | ||
/.php-cs-fixer.cache | ||
/.phpunit.result.cache |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
<?php declare(strict_types=1); | ||
|
||
namespace MLL\Utils; | ||
|
||
use Carbon\Carbon; | ||
|
||
/** | ||
* Some definitions: | ||
* Holiday: special occasions on a mostly fixed date where there is no work | ||
* Weekend Day: Saturday and Sunday | ||
* Business Day: any day that is neither a Holiday nor a Weekend Day. | ||
*/ | ||
class BavarianHolidays | ||
{ | ||
public const HOLIDAYS_STATIC = [ | ||
'01.01' => 'Neujahrstag', | ||
'06.01' => 'Heilige Drei Könige', | ||
'01.05' => 'Tag der Arbeit', | ||
'15.08' => 'Maria Himmelfahrt', | ||
'03.10' => 'Tag der Deutschen Einheit', | ||
'01.11' => 'Allerheiligen', | ||
'24.12' => 'Heilig Abend', | ||
'25.12' => 'Erster Weihnachtstag', | ||
'26.12' => 'Zweiter Weihnachtstag', | ||
'31.12' => 'Sylvester', | ||
]; | ||
|
||
public const SAMSTAG = 'Samstag'; | ||
public const SONNTAG = 'Sonntag'; | ||
public const KARFREITAG = 'Karfreitag'; | ||
public const OSTERSONNTAG = 'Ostersonntag'; | ||
public const OSTERMONTAG = 'Ostermontag'; | ||
public const CHRISTI_HIMMELFAHRT = 'Christi Himmelfahrt'; | ||
public const PFINGSTSONNTAG = 'Pfingstsonntag'; | ||
public const PFINGSTMONTAG = 'Pfingstmontag'; | ||
public const FRONLEICHNAM = 'Fronleichnam'; | ||
public const REFORMATIONSTAG_500_JAHRE_REFORMATION = 'Reformationstag (500 Jahre Reformation)'; | ||
|
||
/** | ||
* Optionally allows users to define extra holidays for a given year. | ||
* | ||
* The returned array is expected to be a map from the day of the year | ||
* (format with @see self::dayOfTheYear()) to holiday names. | ||
* | ||
* @example ['23.02' => 'Day of the Tentacle'] | ||
* | ||
* @var (callable(int): array<string, string>)|null | ||
*/ | ||
public static $loadUserDefinedHolidays; | ||
|
||
/** Checks if given date is a business day. */ | ||
public static function isBusinessDay(Carbon $date): bool | ||
{ | ||
return ! self::isHoliday($date) | ||
&& ! $date->isWeekend(); | ||
} | ||
|
||
/** Checks if given date is a holiday. */ | ||
public static function isHoliday(Carbon $date): bool | ||
{ | ||
return is_string(self::nameHoliday($date)); | ||
} | ||
|
||
/** | ||
* Returns the name of the holiday if the date happens to land on one. | ||
* Saturday and Sunday are not evaluated as holiday. | ||
*/ | ||
public static function nameHoliday(Carbon $date): ?string | ||
{ | ||
$holidayMap = self::buildHolidayMap($date); | ||
|
||
return $holidayMap[self::dayOfTheYear($date)] ?? null; | ||
} | ||
|
||
/** Returns a new carbon instance with the given number of business days added. */ | ||
public static function addBusinessDays(Carbon $date, int $days): Carbon | ||
{ | ||
return DateModification::addDays( | ||
$date, | ||
$days, | ||
fn (Carbon $date): bool => self::isBusinessDay($date) | ||
); | ||
} | ||
|
||
/** Returns a new carbon instance with the given number of business days subtracted. */ | ||
public static function subBusinessDays(Carbon $date, int $days): Carbon | ||
{ | ||
return DateModification::subDays( | ||
$date, | ||
$days, | ||
fn (Carbon $date): bool => self::isBusinessDay($date) | ||
); | ||
} | ||
|
||
/** | ||
* Returns a map from day/month to named holidays. | ||
* | ||
* @return array<string, string> | ||
*/ | ||
protected static function buildHolidayMap(Carbon $date): array | ||
Check warning on line 100 in src/BavarianHolidays.php GitHub Actions / mutation-tests
|
||
{ | ||
$holidays = self::HOLIDAYS_STATIC; | ||
|
||
$year = $date->year; | ||
|
||
// dynamic holidays | ||
// easter_days avoids issues with timezones and is not limited to UNIX timestamps, see https://github.com/briannesbitt/Carbon/pull/1052#issuecomment-381178494 | ||
$easter = Carbon::createMidnightDate($year, 3, 21) | ||
->addDays(easter_days($year)); | ||
$holidays[self::dateFromEaster($easter, -2)] = self::KARFREITAG; | ||
$holidays[self::dateFromEaster($easter, 0)] = self::OSTERSONNTAG; | ||
$holidays[self::dateFromEaster($easter, 1)] = self::OSTERMONTAG; | ||
Check warning on line 112 in src/BavarianHolidays.php GitHub Actions / mutation-tests
|
||
$holidays[self::dateFromEaster($easter, 39)] = self::CHRISTI_HIMMELFAHRT; | ||
Check warning on line 113 in src/BavarianHolidays.php GitHub Actions / mutation-tests
Check warning on line 113 in src/BavarianHolidays.php GitHub Actions / mutation-tests
|
||
$holidays[self::dateFromEaster($easter, 49)] = self::PFINGSTSONNTAG; | ||
Check warning on line 114 in src/BavarianHolidays.php GitHub Actions / mutation-tests
Check warning on line 114 in src/BavarianHolidays.php GitHub Actions / mutation-tests
|
||
$holidays[self::dateFromEaster($easter, 50)] = self::PFINGSTMONTAG; | ||
Check warning on line 115 in src/BavarianHolidays.php GitHub Actions / mutation-tests
Check warning on line 115 in src/BavarianHolidays.php GitHub Actions / mutation-tests
|
||
$holidays[self::dateFromEaster($easter, 60)] = self::FRONLEICHNAM; | ||
Check warning on line 116 in src/BavarianHolidays.php GitHub Actions / mutation-tests
Check warning on line 116 in src/BavarianHolidays.php GitHub Actions / mutation-tests
|
||
|
||
// exceptional holidays | ||
if ($year === 2017) { | ||
$holidays['31.10'] = self::REFORMATIONSTAG_500_JAHRE_REFORMATION; | ||
} | ||
|
||
// user-defined holidays | ||
if (isset(self::$loadUserDefinedHolidays)) { | ||
$holidays = array_merge( | ||
$holidays, | ||
(self::$loadUserDefinedHolidays)($year) | ||
); | ||
} | ||
|
||
return $holidays; | ||
} | ||
|
||
protected static function dateFromEaster(Carbon $easter, int $daysAway): string | ||
{ | ||
$date = $easter->clone()->addDays($daysAway); | ||
|
||
return self::dayOfTheYear($date); | ||
} | ||
|
||
public static function dayOfTheYear(Carbon $date): string | ||
{ | ||
return $date->format('d.m'); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
<?php declare(strict_types=1); | ||
|
||
namespace MLL\Utils; | ||
|
||
use Carbon\Carbon; | ||
|
||
class DateModification | ||
{ | ||
/** @param callable(Carbon): bool $shouldCount should the given date be added? */ | ||
public static function addDays(Carbon $date, int $days, callable $shouldCount): Carbon | ||
{ | ||
// Make sure we do not mutate the original date | ||
$copy = $date->clone(); | ||
|
||
while ($days > 0) { | ||
$copy->addDay(); | ||
if ($shouldCount($copy)) { | ||
--$days; | ||
} | ||
} | ||
|
||
return $copy; | ||
} | ||
|
||
/** @param callable(Carbon): bool $shouldCount should the given date be subtracted? */ | ||
public static function subDays(Carbon $date, int $days, callable $shouldCount): Carbon | ||
{ | ||
// Make sure we do not mutate the original date | ||
$copy = $date->clone(); | ||
|
||
while ($days > 0) { | ||
$copy->subDay(); | ||
if ($shouldCount($copy)) { | ||
--$days; | ||
} | ||
} | ||
|
||
return $copy; | ||
} | ||
} |