feat(services): add generator for public holidays in cz

This commit is contained in:
Ondrej Vlach 2024-08-07 12:54:55 +02:00
parent ef937530af
commit 24f36c16d0
No known key found for this signature in database
GPG Key ID: 7F141CDACEDEE2DE
5 changed files with 155 additions and 0 deletions

View File

@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
namespace App\Services\WorkingDays\PublicHolidays\Generator;
use Nubium\Exception\ThisShouldNeverHappenException;
/**
* Generates public holidays for a given year in Czech Republic.
*/
class PublicHolidaysCzechGenerator implements PublicHolidaysGenerator
{
// non-working days in Czech republic in format [month, day]
protected const array HOLIDAYS = [
[1, 1],
[5, 1],
[5, 8],
[7, 5],
[7, 6],
[9, 28],
[10, 28],
[11, 17],
[12, 24],
[12, 25],
[12, 26]
];
public function generatePublicHolidaysForYear(int $year): array
{
$result = [];
foreach (static::HOLIDAYS as $holiday) {
$formatedDate = sprintf("%d-%02d-%02d 00:00:00", $year, $holiday[0], $holiday[1]);
$date = \DateTimeImmutable::createFromFormat("Y-m-d h:i:s", $formatedDate);
if ($date == false) {
throw new ThisShouldNeverHappenException("Invalid date format: $formatedDate, self::HOLIDAYS or year is incorrect.");
}
$result[] = $date;
}
// Add easters
$easters = $this->getEasterDateTime($year);
$result[] = $easters->add(new \DateInterval("P1D")); // easter Monday
$result[] = $easters->sub(new \DateInterval("P2D")); // easter Friday
array_multisort($result, SORT_ASC);
return $result;
}
private function getEasterDateTime(int $year): \DateTimeImmutable
{
$base = new \DateTime("$year-03-21");
$days = \easter_days($year);
return \DateTimeImmutable::createFromInterface($base->add(new \DateInterval("P{$days}D")));
}
}

View File

@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
namespace App\Services\WorkingDays\PublicHolidays\Generator;
/**
* Generates public holidays.
*/
interface PublicHolidaysGenerator
{
/**
* Generates public holidays for the given year.
* @param int $year
* @return \DateTimeImmutable[]
*/
public function generatePublicHolidaysForYear(int $year): array;
}

View File

@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace App\Services\WorkingDays\PublicHolidays;
use App\Services\WorkingDays\PublicHolidays\Generator\PublicHolidaysCzechGenerator;
use App\Services\WorkingDays\PublicHolidays\Generator\PublicHolidaysGenerator;
/**
* Factory for creating public holidays generators
*/
class PublicHolidaysGeneratorFactory
{
public function createPublicHolidaysGeneratorForCountry(string $countryCode): PublicHolidaysGenerator
{
return match ($countryCode) {
'CZ' => new PublicHolidaysCzechGenerator(),
default => throw new \RuntimeException('Unsupported country code'),
};
}
}

View File

@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace Tests\Unit\Services\WorkingDays\PublicHolidays\Generator;
use App\Services\WorkingDays\PublicHolidays\Generator\PublicHolidaysCzechGenerator;
use PHPUnit\Framework\TestCase;
class PublicHolidaysCzechGeneratorTest extends TestCase
{
public function testCzechPublicHolidays(): void
{
$czechNWD = new PublicHolidaysCzechGenerator();
// generate non-working days for 2025 and assert the results
$czechNWD = $czechNWD->generatePublicHolidaysForYear(2025);
$this->assertEquals([
new \DateTimeImmutable("2025-01-01"),
new \DateTimeImmutable("2025-04-18"),
new \DateTimeImmutable("2025-04-21"),
new \DateTimeImmutable("2025-05-01"),
new \DateTimeImmutable("2025-05-08"),
new \DateTimeImmutable("2025-07-05"),
new \DateTimeImmutable("2025-07-06"),
new \DateTimeImmutable("2025-09-28"),
new \DateTimeImmutable("2025-10-28"),
new \DateTimeImmutable("2025-11-17"),
new \DateTimeImmutable("2025-12-24"),
new \DateTimeImmutable("2025-12-25"),
new \DateTimeImmutable("2025-12-26"),
], $czechNWD);
}
}

View File

@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace Tests\Unit\Services\WorkingDays;
use App\Services\WorkingDays\PublicHolidays\Generator\PublicHolidaysCzechGenerator;
use App\Services\WorkingDays\PublicHolidays\PublicHolidaysGeneratorFactory;
use Tests\TestCase;
class PublicHolidaysGeneratorFactoryTest extends TestCase
{
public function testPublicHolidaysGeneratorFactoryWillReturnCzechGeneratorForCZ(): void
{
$factory = new PublicHolidaysGeneratorFactory();
$this->assertInstanceOf(PublicHolidaysCzechGenerator::class, $factory->createPublicHolidaysGeneratorForCountry('CZ'));
}
public function testRuntimeExceptionGeneratedForUnknownState(): void
{
$factory = new PublicHolidaysGeneratorFactory();
$this->expectException(\RuntimeException::class);
$factory->createPublicHolidaysGeneratorForCountry('XZ');
}
}