56 lines
		
	
	
		
			1.8 KiB
		
	
	
	
		
			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;
 | 
						|
    }
 | 
						|
}
 |