Skip to content

Commit

Permalink
#88 Add the ability to overflow times into the next day (#104)
Browse files Browse the repository at this point in the history
* Add asStructuredData to README usage

* Allow on construction for setting a flag for overlapping times. A night club opens that opens till 3am on Friday and Saturday. And modify the API to search for yesterday

* Oops, change docs to use overflowing rather than overlapping

* Fix styleguide
  • Loading branch information
rlweb authored and kylekatarnls committed May 7, 2019
1 parent b6b141a commit 03d2d44
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 13 deletions.
23 changes: 21 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,17 @@ $openingHours->forDate(new DateTime('2016-12-25'));
$openingHours->exceptions();
```

On construction you can set a flag for overflowing times across days. For example, for a night club opens that opens till 3am on Friday and Saturday:

```php
$openingHours = \Spatie\OpeningHours\OpeningHours::create([
'friday' => ['20:00-03:00'],
'saturday' => ['20:00-03:00'],
], null, true);
```

This allows the API to further at yesterdays data to check if the opening hours are open from yesterdays time range.

You can add data in definitions then retrieve them:

```php
Expand Down Expand Up @@ -190,7 +201,7 @@ The package should only be used through the `OpeningHours` class. There are also

### `Spatie\OpeningHours\OpeningHours`

#### `OpeningHours::create(array $data): Spatie\OpeningHours\OpeningHours`
#### `OpeningHours::create(array $data, $timezone = null, bool $overflow = false): Spatie\OpeningHours\OpeningHours`

Static factory method to fill the set of opening hours.

Expand All @@ -203,7 +214,7 @@ $openingHours = OpeningHours::create([

#### `OpeningHours::mergeOverlappingRanges(array $schedule) : array`

For safety sake, creating `OpeningHours` object with overlapping ranges will throw an exception. But you can explicitly merge them.
For safety sake, creating `OpeningHours` object with overlapping ranges will throw an exception. But you can explicitly merge them. This will not cope with overflowing times across days.

``` php
$ranges = [
Expand Down Expand Up @@ -333,6 +344,14 @@ Returns next close DateTime from the given DateTime
$openingHours->nextClose(new DateTime('2016-12-24 11:00:00'));
```

#### `asStructuredData() : array`

Returns a (OpeningHoursSpecification)[https://schema.org/openingHoursSpecification] as an array.

```php
$openingHours->asStructuredData();
```

### `Spatie\OpeningHours\OpeningHoursForDay`

This class is meant as read-only. It implements `ArrayAccess`, `Countable` and `IteratorAggregate` so you can process the list of `TimeRange`s in an array-like way.
Expand Down
61 changes: 50 additions & 11 deletions src/OpeningHours.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,23 +28,34 @@ class OpeningHours
/** @var DateTimeZone|null */
protected $timezone = null;

public function __construct($timezone = null)
/** @var bool Allow for overflowing time ranges which overflow into the next day */
private $overflow;

public function __construct($timezone = null, bool $overflow = false)
{
$this->timezone = $timezone ? new DateTimeZone($timezone) : null;
if ($timezone instanceof DateTimeZone) {
$this->timezone = $timezone;
} elseif (is_string($timezone)) {
$this->timezone = new DateTimeZone($timezone);
} elseif ($timezone) {
throw new \InvalidArgumentException('Invalid Timezone');
}
$this->overflow = $overflow;

$this->openingHours = Day::mapDays(function () {
return new OpeningHoursForDay();
});
}

/**
* @param array $data
*
* @param string[][] $data
* @param string|DateTimeZone|null $timezone
* @param bool $overflow
* @return static
*/
public static function create(array $data)
public static function create(array $data, $timezone = null, bool $overflow = false): self
{
return (new static())->fill($data);
return (new static($timezone, $overflow))->fill($data);
}

/**
Expand Down Expand Up @@ -94,13 +105,14 @@ public static function mergeOverlappingRanges(array $data)
}

/**
* @param array $data
*
* @param string[][] $data
* @param string|DateTimeZone|null $timezone
* @param bool $overflow
* @return static
*/
public static function createAndMergeOverlappingRanges(array $data)
public static function createAndMergeOverlappingRanges(array $data, $timezone = null, bool $overflow = false)
{
return static::create(static::mergeOverlappingRanges($data));
return static::create(static::mergeOverlappingRanges($data), $timezone, $overflow);
}

/**
Expand Down Expand Up @@ -213,6 +225,18 @@ public function isOpenAt(DateTimeInterface $dateTime): bool
{
$dateTime = $this->applyTimezone($dateTime);

if ($this->overflow) {
$yesterdayDateTime = $dateTime;
if (! ($yesterdayDateTime instanceof DateTimeImmutable)) {
$yesterdayDateTime = clone $yesterdayDateTime;
}
$dateTimeMinus1Day = $yesterdayDateTime->sub(new \DateInterval('P1D'));
$openingHoursForDayBefore = $this->forDate($dateTimeMinus1Day);
if ($openingHoursForDayBefore->isOpenAt(Time::fromDateTime($dateTimeMinus1Day))) {
return true;
}
}

$openingHoursForDay = $this->forDate($dateTime);

return $openingHoursForDay->isOpenAt(Time::fromDateTime($dateTime));
Expand Down Expand Up @@ -272,8 +296,23 @@ public function nextClose(DateTimeInterface $dateTime): DateTimeInterface
$dateTime = clone $dateTime;
}

$nextClose = null;
if ($this->overflow) {
$yesterday = $dateTime;
if (! ($dateTime instanceof DateTimeImmutable)) {
$yesterday = clone $dateTime;
}
$dateTimeMinus1Day = $yesterday->sub(new \DateInterval('P1D'));
$openingHoursForDayBefore = $this->forDate($dateTimeMinus1Day);
if ($openingHoursForDayBefore->isOpenAt(Time::fromDateTime($dateTimeMinus1Day))) {
$nextClose = $openingHoursForDayBefore->nextClose(Time::fromDateTime($dateTime));
}
}

$openingHoursForDay = $this->forDate($dateTime);
$nextClose = $openingHoursForDay->nextClose(Time::fromDateTime($dateTime));
if (! $nextClose) {
$nextClose = $openingHoursForDay->nextClose(Time::fromDateTime($dateTime));
}

while ($nextClose === false || $nextClose->hours() >= 24) {
$dateTime = $dateTime
Expand Down
69 changes: 69 additions & 0 deletions tests/OpeningHoursOverflowTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

namespace Spatie\OpeningHours\Test;

use DateTime;
use DateTimeImmutable;
use PHPUnit\Framework\TestCase;
use Spatie\OpeningHours\TimeRange;
use Spatie\OpeningHours\OpeningHours;

class OpeningHoursOverflowTest extends TestCase
{
/** @test */
public function it_fills_opening_hours_with_overflow()
{
$openingHours = OpeningHours::create([
'monday' => ['09:00-02:00'],
], null, true);

$this->assertInstanceOf(TimeRange::class, $openingHours->forDay('monday')[0]);
$this->assertEquals((string) $openingHours->forDay('monday')[0], '09:00-02:00');
}

/** @test */
public function check_open_with_overflow()
{
$openingHours = OpeningHours::create([
'monday' => ['09:00-02:00'],
], null, true);

$shouldBeOpen = new DateTime('2019-04-23 01:00:00');
$this->assertTrue($openingHours->isOpenAt($shouldBeOpen));
}

/** @test */
public function check_open_with_overflow_immutable()
{
$openingHours = OpeningHours::create([
'monday' => ['09:00-02:00'],
], null, true);

$shouldBeOpen = new DateTimeImmutable('2019-04-23 01:00:00');
$this->assertTrue($openingHours->isOpenAt($shouldBeOpen));
}

/** @test */
public function next_close_with_overflow()
{
$openingHours = OpeningHours::create([
'monday' => ['09:00-02:00'],
], null, true);

$shouldBeOpen = new DateTime('2019-04-23 01:00:00');
$this->assertEquals('2019-04-23 02:00:00', $openingHours->nextClose($shouldBeOpen)->format('Y-m-d H:i:s'));
}

/** @test */
public function next_close_with_overflow_immutable()
{
$openingHours = OpeningHours::create([
'monday' => ['09:00-02:00'],
], null, true);

$shouldBeOpen = new DateTimeImmutable('2019-04-23 01:00:00');
$nextTimeClosed = $openingHours->nextClose($shouldBeOpen)->format('Y-m-d H:i:s');
$this->assertEquals('2019-04-23 02:00:00', $nextTimeClosed);
$this->assertEquals('2019-04-23 01:00:00', $shouldBeOpen->format('Y-m-d H:i:s'));
}
}

0 comments on commit 03d2d44

Please sign in to comment.