diff --git a/src/Exceptions/InvalidDateTimeClass.php b/src/Exceptions/InvalidDateTimeClass.php new file mode 100644 index 0000000..726c9fb --- /dev/null +++ b/src/Exceptions/InvalidDateTimeClass.php @@ -0,0 +1,11 @@ +dateTimeClass = $dateTimeClass ?? DateTime::class; + + return $this; + } + /** * Set the number of days to try before abandoning the search of the next close/open time. * - * @param int $dayLimit number of days + * @param int $dayLimit number of days + * @return $this */ public function setDayLimit(int $dayLimit) { $this->dayLimit = $dayLimit; + + return $this; } /** @@ -176,7 +199,8 @@ public function getFilters(): array public function fill(array $data) { - list($openingHours, $exceptions, $metaData, $filters, $overflow) = $this->parseOpeningHoursAndExceptions($data); + list($openingHours, $exceptions, $metaData, $filters, $overflow, $dateTimeClass) = $this + ->parseOpeningHoursAndExceptions($data); $this->overflow = $overflow; @@ -186,7 +210,7 @@ public function fill(array $data) $this->setExceptionsFromStrings($exceptions); - return $this->setFilters($filters)->setData($metaData); + return $this->setDateTimeClass($dateTimeClass)->setFilters($filters)->setData($metaData); } public function forWeek(): array @@ -261,8 +285,7 @@ public function forDate(DateTimeInterface $date): OpeningHoursForDay } /** - * @param DateTimeInterface $date - * + * @param DateTimeInterface $date * @return TimeRange[] */ public function forDateTime(DateTimeInterface $date): array @@ -286,7 +309,7 @@ public function isOpenOn(string $day): bool list(, $year, $month, $day) = $match; $year = $year ?: date('Y'); - return count($this->forDate(new DateTime("$year-$month-$day", $this->timezone))) > 0; + return count($this->forDate(new DateTimeImmutable("$year-$month-$day", $this->timezone))) > 0; } return count($this->forDay($day)) > 0; @@ -321,12 +344,12 @@ public function isClosedAt(DateTimeInterface $dateTime): bool public function isOpen(): bool { - return $this->isOpenAt(new DateTime()); + return $this->isOpenAt(new $this->dateTimeClass()); } public function isClosed(): bool { - return $this->isClosedAt(new DateTime()); + return $this->isClosedAt(new $this->dateTimeClass()); } public function currentOpenRange(DateTimeInterface $dateTime) @@ -376,8 +399,9 @@ public function currentOpenRangeEnd(DateTimeInterface $dateTime) return $dateTime->setTime($nextDateTime->format('G'), $nextDateTime->format('i'), 0); } - public function nextOpen(DateTimeInterface $dateTime): DateTimeInterface + public function nextOpen(DateTimeInterface $dateTime = null): DateTimeInterface { + $dateTime = $dateTime ?? new $this->dateTimeClass(); $dateTime = $this->copyDateTime($dateTime); $openingHoursForDay = $this->forDate($dateTime); $nextOpen = $openingHoursForDay->nextOpen(Time::fromDateTime($dateTime)); @@ -413,8 +437,9 @@ public function nextOpen(DateTimeInterface $dateTime): DateTimeInterface return $dateTime->setTime($nextDateTime->format('G'), $nextDateTime->format('i'), 0); } - public function nextClose(DateTimeInterface $dateTime): DateTimeInterface + public function nextClose(DateTimeInterface $dateTime = null): DateTimeInterface { + $dateTime = $dateTime ?? new $this->dateTimeClass(); $dateTime = $this->copyDateTime($dateTime); $nextClose = null; if ($this->overflow) { @@ -569,6 +594,7 @@ public function setTimezone($timezone) protected function parseOpeningHoursAndExceptions(array $data): array { + $dateTimeClass = Arr::pull($data, 'dateTimeClass', null); $metaData = Arr::pull($data, 'data', null); $exceptions = []; $filters = Arr::pull($data, 'filters', []); @@ -590,7 +616,7 @@ protected function parseOpeningHoursAndExceptions(array $data): array $openingHours[$this->normalizeDayName($day)] = $openingHoursData; } - return [$openingHours, $exceptions, $metaData, $filters, $overflow]; + return [$openingHours, $exceptions, $metaData, $filters, $overflow, $dateTimeClass]; } protected function setOpeningHoursFromStrings(string $day, array $openingHours) diff --git a/src/OpeningHoursForDay.php b/src/OpeningHoursForDay.php index 9372ef3..e687e7d 100644 --- a/src/OpeningHoursForDay.php +++ b/src/OpeningHoursForDay.php @@ -71,9 +71,8 @@ public function isOpenAtNight(Time $time) } /** - * @param callable[] $filters - * @param bool $reverse - * + * @param callable[] $filters + * @param bool $reverse * @return Time|bool */ public function openingHoursFilter(array $filters, bool $reverse = false) @@ -92,8 +91,7 @@ public function openingHoursFilter(array $filters, bool $reverse = false) } /** - * @param Time $time - * + * @param Time $time * @return bool|Time */ public function nextOpen(Time $time) @@ -106,8 +104,7 @@ function ($timeRange) use ($time) { } /** - * @param Time $time - * + * @param Time $time * @return bool|TimeRange */ public function nextOpenRange(Time $time) @@ -120,8 +117,7 @@ function ($timeRange) use ($time) { } /** - * @param Time $time - * + * @param Time $time * @return bool|Time */ public function nextClose(Time $time) @@ -137,8 +133,7 @@ function ($timeRange) use ($time) { } /** - * @param Time $time - * + * @param Time $time * @return bool|TimeRange */ public function nextCloseRange(Time $time) @@ -154,8 +149,7 @@ function ($timeRange) use ($time) { } /** - * @param Time $time - * + * @param Time $time * @return bool|Time */ public function previousOpen(Time $time) @@ -171,8 +165,7 @@ function ($timeRange) use ($time) { } /** - * @param Time $time - * + * @param Time $time * @return bool|TimeRange */ public function previousOpenRange(Time $time) @@ -185,8 +178,7 @@ function ($timeRange) use ($time) { } /** - * @param Time $time - * + * @param Time $time * @return bool|Time */ public function previousClose(Time $time) @@ -199,8 +191,7 @@ function ($timeRange) use ($time) { } /** - * @param Time $time - * + * @param Time $time * @return bool|TimeRange */ public function previousCloseRange(Time $time) @@ -251,8 +242,7 @@ public function getIterator() } /** - * @param Time $time - * + * @param Time $time * @return TimeRange[] */ public function forTime(Time $time): Generator @@ -267,8 +257,7 @@ public function forTime(Time $time): Generator } /** - * @param Time $time - * + * @param Time $time * @return TimeRange[] */ public function forNightTime(Time $time): Generator diff --git a/tests/OpeningHoursCustomClassTest.php b/tests/OpeningHoursCustomClassTest.php new file mode 100644 index 0000000..3a9067c --- /dev/null +++ b/tests/OpeningHoursCustomClassTest.php @@ -0,0 +1,74 @@ + ['09:00-18:00'], + 'dateTimeClass' => DateTimeImmutable::class, + ]); + + $date = $openingHours->nextOpen(new DateTimeImmutable('2021-10-11 04:30')); + + $this->assertInstanceOf(DateTimeImmutable::class, $date); + $this->assertSame('2021-10-11 09:00:00', $date->format('Y-m-d H:i:s')); + } + + /** @test */ + public function it_can_use_mocked_time() + { + $mock1 = new class extends DateTimeImmutable + { + public function __construct($datetime = 'now', DateTimeZone $timezone = null) + { + parent::__construct('2021-10-11 04:30', $timezone); + } + }; + $mock2 = new class extends DateTimeImmutable + { + public function __construct($datetime = 'now', DateTimeZone $timezone = null) + { + parent::__construct('2021-10-11 09:30', $timezone); + } + }; + + $openingHours = OpeningHours::create([ + 'monday' => ['09:00-18:00'], + 'dateTimeClass' => get_class($mock1), + ]); + + $this->assertFalse($openingHours->isOpen()); + + $openingHours = OpeningHours::create([ + 'monday' => ['09:00-18:00'], + 'dateTimeClass' => get_class($mock2), + ]); + + $this->assertTrue($openingHours->isOpen()); + } + + /** @test */ + public function it_should_refuse_invalid_date_time_class() + { + $this->expectException(InvalidDateTimeClass::class); + OpeningHours::create([ + 'monday' => ['09:00-18:00'], + 'dateTimeClass' => DateTimeZone::class, + ]); + } +}