feat : add working day calculator (weekend + holiday aware)
This commit is contained in:
@@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Service\Notification;
|
||||||
|
|
||||||
|
use App\Service\PublicHolidayServiceInterface;
|
||||||
|
use DateTimeImmutable;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
final readonly class WorkingDayCalculator
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private PublicHolidayServiceInterface $holidays,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function isWorkingDay(DateTimeImmutable $date): bool
|
||||||
|
{
|
||||||
|
$dayOfWeek = (int) $date->format('N'); // 1 (lundi) .. 7 (dimanche)
|
||||||
|
if ($dayOfWeek >= 6) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !$this->isPublicHoliday($date);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function nextWorkingDay(DateTimeImmutable $date): DateTimeImmutable
|
||||||
|
{
|
||||||
|
$candidate = $date->modify('+1 day')->setTime(0, 0, 0);
|
||||||
|
while (!$this->isWorkingDay($candidate)) {
|
||||||
|
$candidate = $candidate->modify('+1 day');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $candidate;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function isPublicHoliday(DateTimeImmutable $date): bool
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$holidays = $this->holidays->getHolidaysDayByYears('metropole', $date->format('Y'));
|
||||||
|
} catch (Throwable) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isset($holidays[$date->format('Y-m-d')]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Tests\Service\Notification;
|
||||||
|
|
||||||
|
use App\Service\Notification\WorkingDayCalculator;
|
||||||
|
use App\Service\PublicHolidayServiceInterface;
|
||||||
|
use DateTimeImmutable;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
final class WorkingDayCalculatorTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testWeekdayIsWorkingDay(): void
|
||||||
|
{
|
||||||
|
// Mardi 08/07/2025
|
||||||
|
self::assertTrue($this->calculator()->isWorkingDay(new DateTimeImmutable('2025-07-08')));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSaturdayAndSundayAreNotWorkingDays(): void
|
||||||
|
{
|
||||||
|
self::assertFalse($this->calculator()->isWorkingDay(new DateTimeImmutable('2025-07-12'))); // samedi
|
||||||
|
self::assertFalse($this->calculator()->isWorkingDay(new DateTimeImmutable('2025-07-13'))); // dimanche
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPublicHolidayIsNotWorkingDay(): void
|
||||||
|
{
|
||||||
|
self::assertFalse($this->calculator()->isWorkingDay(new DateTimeImmutable('2025-07-14'))); // lundi férié
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNextWorkingDayFromWeekdayIsTomorrow(): void
|
||||||
|
{
|
||||||
|
// Mardi 08/07 -> Mercredi 09/07
|
||||||
|
self::assertSame(
|
||||||
|
'2025-07-09',
|
||||||
|
$this->calculator()->nextWorkingDay(new DateTimeImmutable('2025-07-08'))->format('Y-m-d')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNextWorkingDayFromFridaySkipsWeekend(): void
|
||||||
|
{
|
||||||
|
// Vendredi 11/07 -> lundi 14/07 est férié -> mardi 15/07
|
||||||
|
self::assertSame(
|
||||||
|
'2025-07-15',
|
||||||
|
$this->calculator()->nextWorkingDay(new DateTimeImmutable('2025-07-11'))->format('Y-m-d')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function calculator(): WorkingDayCalculator
|
||||||
|
{
|
||||||
|
$holidays = $this->createStub(PublicHolidayServiceInterface::class);
|
||||||
|
$holidays->method('getHolidaysDayByYears')->willReturn([
|
||||||
|
// Lundi 14/07/2025 férié
|
||||||
|
'2025-07-14' => 'Fête nationale',
|
||||||
|
]);
|
||||||
|
|
||||||
|
return new WorkingDayCalculator($holidays);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user