ogsoft-example/app/Services/WorkingDays/DurationSolver.php

56 lines
1.8 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Services\WorkingDays;
use App\Services\Utils\DurationConvertor;
/**
* Solves the duration of a given period based on working days and return new end date.
*/
class DurationSolver
{
protected const int MAX_ITERATIONS = 100;
public function __construct(
protected readonly WorkingDayDeterminerInterface $workingDayDeterminer,
protected readonly DurationConvertor $durationConvertor,
) {
}
/**
* Calculate the duration of a given period based on working days.
* @param \DateTimeImmutable $startDate
* @param \DateTimeImmutable $endDate
* @return \DateTimeImmutable
* @throws \Exception
*/
public function calculateDuration(
\DateTimeImmutable $startDate,
\DateTimeImmutable $endDate,
): \DateTimeImmutable {
$loopId = 0;
$requiredWorkingDaysCount = (int) ($startDate->setTime(0, 0)->diff($endDate->setTime(0, 0))->format('%a')) + 1;
do {
$workingDaysCount = (int) ($startDate->setTime(0, 0)->diff($endDate->setTime(0, 0))->format('%a')) + 1;
$workingDaysCountInInterval = $this->workingDayDeterminer->getWorkingDaysCount($startDate, $workingDaysCount);
if ($workingDaysCountInInterval >= $requiredWorkingDaysCount) {
// there is no weekends or holidays in the given interval
break;
} else {
$endDate = $endDate->add(new \DateInterval(sprintf('P%dD', $requiredWorkingDaysCount - $workingDaysCountInInterval)));
if ($loopId > static::MAX_ITERATIONS) {
throw new \InvalidArgumentException('No working days found in the given interval');
}
}
$loopId++;
} while ($workingDaysCount != $workingDaysCountInInterval);
return $endDate;
}
}