107 lines
3.0 KiB
PHP
107 lines
3.0 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Service\Leave;
|
|
|
|
use App\Entity\ContractSuspension;
|
|
use DateTimeImmutable;
|
|
|
|
final class SuspensionDaysCalculator
|
|
{
|
|
/**
|
|
* Count calendar days suspended within a month window [monthStart, monthEnd].
|
|
*
|
|
* @param list<ContractSuspension> $suspensions
|
|
*/
|
|
public function countSuspendedDaysInMonth(
|
|
DateTimeImmutable $monthStart,
|
|
DateTimeImmutable $monthEnd,
|
|
array $suspensions
|
|
): int {
|
|
$total = 0;
|
|
|
|
foreach ($suspensions as $suspension) {
|
|
$sStart = $suspension->getStartDate();
|
|
$sEnd = $suspension->getEndDate() ?? $monthEnd;
|
|
|
|
$overlapStart = $sStart > $monthStart ? $sStart : $monthStart;
|
|
$overlapEnd = $sEnd < $monthEnd ? $sEnd : $monthEnd;
|
|
|
|
if ($overlapStart > $overlapEnd) {
|
|
continue;
|
|
}
|
|
|
|
$total += ((int) $overlapEnd->diff($overlapStart)->format('%a')) + 1;
|
|
}
|
|
|
|
return $total;
|
|
}
|
|
|
|
/**
|
|
* Return adjusted suspensions where the first month of each suspension is excluded (grace period).
|
|
*
|
|
* @param list<ContractSuspension> $suspensions
|
|
*
|
|
* @return list<ContractSuspension>
|
|
*/
|
|
public function applyFirstMonthGrace(array $suspensions): array
|
|
{
|
|
$adjusted = [];
|
|
|
|
foreach ($suspensions as $suspension) {
|
|
$gracedStart = $suspension->getStartDate()->modify('+1 month');
|
|
$end = $suspension->getEndDate();
|
|
|
|
if ($end instanceof DateTimeImmutable && $gracedStart > $end) {
|
|
continue;
|
|
}
|
|
|
|
$copy = new ContractSuspension();
|
|
$copy->setStartDate($gracedStart);
|
|
$copy->setEndDate($end);
|
|
|
|
$adjusted[] = $copy;
|
|
}
|
|
|
|
return $adjusted;
|
|
}
|
|
|
|
/**
|
|
* Count business days (Mon-Fri, excl. public holidays) suspended within a period.
|
|
*
|
|
* @param list<ContractSuspension> $suspensions
|
|
* @param array<string, string> $publicHolidays map of Y-m-d => label
|
|
*/
|
|
public function countSuspendedBusinessDays(
|
|
DateTimeImmutable $periodStart,
|
|
DateTimeImmutable $periodEnd,
|
|
array $suspensions,
|
|
array $publicHolidays
|
|
): int {
|
|
$total = 0;
|
|
|
|
foreach ($suspensions as $suspension) {
|
|
$sStart = $suspension->getStartDate();
|
|
$sEnd = $suspension->getEndDate() ?? $periodEnd;
|
|
|
|
$overlapStart = $sStart > $periodStart ? $sStart : $periodStart;
|
|
$overlapEnd = $sEnd < $periodEnd ? $sEnd : $periodEnd;
|
|
|
|
if ($overlapStart > $overlapEnd) {
|
|
continue;
|
|
}
|
|
|
|
for ($cursor = $overlapStart; $cursor <= $overlapEnd; $cursor = $cursor->modify('+1 day')) {
|
|
$weekDay = (int) $cursor->format('N');
|
|
$dayKey = $cursor->format('Y-m-d');
|
|
if ($weekDay <= 5 && !isset($publicHolidays[$dayKey])) {
|
|
++$total;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $total;
|
|
}
|
|
}
|