Lines 100.00% 11 / 11
Functions and Methods 100.00% 4 / 4
Classes and Traits 100.00% 1 / 1
Name Lines Functions and Methods CRAP Classes and Traits
TimeWindow 100.00% 11 / 11 100.00% 4 / 4 8 100.00% 1 / 1
 __construct 100.00% 1 / 1 100.00% 1 / 1 1
 between 100.00% 5 / 5 100.00% 1 / 1 2
 includes 100.00% 4 / 4 100.00% 1 / 1 4
 crossesMidnight 100.00% 1 / 1 100.00% 1 / 1 1
1<?php
2
3declare(strict_types=1);
4
5namespace Stewart\Support\Schedule;
6
7use DateTimeInterface;
8use Stewart\Contracts\Exception\ScheduleError;
9use Stewart\Contracts\Exception\ScheduleException;
10use Stewart\Contracts\Schedule\TimeOfDay;
11
12/** @internal */
13final readonly class TimeWindow
14{
15    private function __construct(
16        public TimeOfDay $start,
17        public TimeOfDay $end,
18    ) {}
19
20    /** @throws ScheduleException */
21    public static function between(TimeOfDay|string $start, TimeOfDay|string $end): self
22    {
23        $start = TimeOfDay::fromTimeOrString($start);
24        $end = TimeOfDay::fromTimeOrString($end);
25
26        if ($start->equals($end)) {
27            throw ScheduleException::createForReason(ScheduleError::TimeWindowEmpty, time: $start->format());
28        }
29
30        return new self($start, $end);
31    }
32
33    public function includes(DateTimeInterface $moment): bool
34    {
35        $seconds = TimeOfDay::fromDateTime($moment)->toSecondsOfDay();
36        $afterStart = $seconds >= $this->start->toSecondsOfDay();
37        $beforeEnd = $seconds < $this->end->toSecondsOfDay();
38
39        return $this->crossesMidnight() ? $afterStart || $beforeEnd : $afterStart && $beforeEnd;
40    }
41
42    public function crossesMidnight(): bool
43    {
44        return $this->end->toSecondsOfDay() < $this->start->toSecondsOfDay();
45    }
46}