<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\DTO\DurationResult;
|
|
use App\Enums\DurationModifier;
|
|
use App\Traits\Invokable;
|
|
use DateInterval;
|
|
use DatePeriod;
|
|
use DateTime;
|
|
use DateTimeImmutable;
|
|
use ValueError;
|
|
use TypeError;
|
|
use Illuminate\Contracts\Container\BindingResolutionException;
|
|
use Spatie\LaravelData\Exceptions\CannotCreateData;
|
|
use Spatie\LaravelData\Exceptions\CannotSetComputedValue;
|
|
|
|
/** @package App\Services */
|
|
class CalculateDuration
|
|
{
|
|
const WEEKDAYS = [1, 2, 3, 4, 5];
|
|
|
|
/**
|
|
* @param DateTime $start
|
|
* @param DateTime $end
|
|
*
|
|
* @return void
|
|
*/
|
|
public function __construct(
|
|
protected DateTimeImmutable $start,
|
|
protected DateTimeImmutable $end
|
|
) {
|
|
}
|
|
|
|
/**
|
|
* @param DurationModifier $durationModifier
|
|
*
|
|
* @return DurationResult
|
|
*
|
|
* @throws ValueError
|
|
* @throws TypeError
|
|
* @throws BindingResolutionException
|
|
* @throws CannotCreateData
|
|
* @throws CannotSetComputedValue
|
|
*/
|
|
public function result(DurationModifier $durationModifier = DurationModifier::None): DurationResult
|
|
{
|
|
$days = $this->calculateDuration();
|
|
$weekDays = $this->calculateDuration(true);
|
|
$weeks = $this->calculateDurationWeeks();
|
|
|
|
$durationModifierInDays = $durationModifier->durationModiferInDays();
|
|
|
|
return new DurationResult(
|
|
days: $days * $durationModifierInDays,
|
|
weeks: $weeks * ($durationModifier === DurationModifier::None ? 1 : $durationModifierInDays * 7),
|
|
weekDays: $weekDays * $durationModifierInDays,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @param string $intervalDuration
|
|
* @param bool $skipWeekends
|
|
*
|
|
* @return int
|
|
*/
|
|
protected function calculateDuration(bool $skipWeekends = false): int
|
|
{
|
|
$interval = new DateInterval('P1D');
|
|
|
|
$period = new DatePeriod(
|
|
$this->start,
|
|
$interval,
|
|
$this->end->add($interval),
|
|
);
|
|
|
|
$intervalCount = 0;
|
|
foreach ($period as $date) {
|
|
if ($skipWeekends && !in_array($date->format('N'), self::WEEKDAYS)) {
|
|
continue;
|
|
}
|
|
|
|
$intervalCount++;
|
|
}
|
|
|
|
return $intervalCount;
|
|
}
|
|
|
|
/**
|
|
* @return int
|
|
*/
|
|
protected function calculateDurationWeeks(): int
|
|
{
|
|
return floor($this->start->diff($this->end)->days / 7);
|
|
}
|
|
}
|