Simple problems require simple solutions
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

96 lines
2.4 KiB

<?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);
}
}