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