Repository: spatie/opening-hours (php)

Issue:
nextClose Method Returns Incorrect Time when Overflow Enabled
Version: 4.0

The nextClose method does not return the expected result when using overflow and dealing with overnight hours. Below are some examples demonstrating the issue.

**Example 1: Working as Expected**
``` php
$openingHours = OpeningHours::create([
   'overflow' => true,
   'monday' => ['18:00-05:00'], // 2024-11-11
   'tuesday' => ['17:00-06:00'], // 2024-11-12
]);

$tuesday = new DateTime('2024-11-12 04:00:00');
echo 'Today      : ' . $tuesday->format('Y-m-d H:i') . "\n";
echo 'Next close : ' . $openingHours->nextClose($tuesday)->format('Y-m-d H:i') . "\n";
```

Output:
```
Today : 2024-11-12 04:00 
Next close : 2024-11-12 05:00
```
This works as expected.

**Example 2: Issue with Overnight Hours**
When the time is set to "2024-11-12 05:30:00" (Which is currently closed)

``` php
$openingHours = OpeningHours::create([
   'overflow' => true,
   'monday' => ['18:00-05:00'], // 2024-11-11
   'tuesday' => ['17:00-06:00'], // 2024-11-12
]);

$tuesday = new DateTime('2024-11-12 05:30:00');
echo 'Today      : ' . $tuesday->format('Y-m-d H:i') . "\n";
echo 'Next close : ' . $openingHours->nextClose($tuesday)->format('Y-m-d H:i') . "\n";
```
Output:
```
Today : 2024-11-12 04:00 
Next close : 2024-11-12 06:00
```
Expected Output:
```
Today : 2024-11-12 04:00 
Next close : 2024-11-13 06:00
```
It incorrectly calculates the next closing time as the current day's 6:00 AM instead of the following day's.

**Example 3: Issue with Multiple Ranges and Overflow**

``` php
$openingHours = OpeningHours::create([
   'overflow' => true,
   'monday' => ['18:00-22:00', '23:00-05:00''], // 2024-11-11
   'tuesday' => ['17:00-06:00'], // 2024-11-12
]);

$tuesday = new DateTime('2024-11-12 04:00:00');
echo 'Today      : ' . $tuesday->format('Y-m-d H:i') . "\n";
echo 'Next close : ' . $openingHours->nextClose($tuesday)->format('Y-m-d H:i') . "\n";
```
Output:
```
Today : 2024-11-12 04:00 
Next close : 2024-11-12 22:00
```
Expected Output:
```
Today : 2024-11-12 04:00 
Next close : 2024-11-12 05:00
```

The next closing time should be 2024-11-12 05:00 but instead incorrectly jumps to the evening time range.

The nextClose method sometimes does not handle overnight hours correctly when overflow is enabled, particularly when the time is outside of any active range. It either skips to the wrong range or calculates the closing time incorrectly.

Pull request:
Fix nextClose() result with overflow over night
Fix #258

Patch:
diff --git a/src/OpeningHours.php b/src/OpeningHours.php
index aa5ba51..c834936 100644
--- a/src/OpeningHours.php
+++ b/src/OpeningHours.php
@@ -525,6 +525,12 @@ class OpeningHours
         $outputTimezone = $this->getOutputTimezone($dateTime);
         $dateTime = $this->applyTimezone($dateTime ?? new $this->dateTimeClass());
         $dateTime = $this->copyDateTime($dateTime);
+        $openRangeEnd = $this->currentOpenRange($dateTime)?->end();
+
+        if ($openRangeEnd && $openRangeEnd->hours() < 24) {
+            return $openRangeEnd->date() ?? $openRangeEnd->toDateTime($dateTime);
+        }
+
         $nextClose = null;
 
         if ($this->overflow) {
@@ -541,7 +547,14 @@ class OpeningHours
         if (! $nextClose) {
             $nextClose = $openingHoursForDay->nextClose(PreciseTime::fromDateTime($dateTime));
 
-            if ($nextClose && $nextClose->hours() < 24 && $nextClose->format('Gi') < $dateTime->format('Gi')) {
+            if (
+                $nextClose
+                && $nextClose->hours() < 24
+                && (
+                    $nextClose->format('Gi') < $dateTime->format('Gi')
+                    || ($this->isClosedAt($dateTime) && $this->nextOpen($dateTime)->format('Gi') > $nextClose->format('Gi'))
+                )
+            ) {
                 $dateTime = $dateTime->modify('+1 day');
             }
         }
diff --git a/src/Time.php b/src/Time.php
index 8c2c903..d7f6496 100644
--- a/src/Time.php
+++ b/src/Time.php
@@ -45,7 +45,7 @@ readonly class Time implements TimeDataContainer
         return $this->minutes;
     }
 
-    public function date(): DateTimeInterface
+    public function date(): ?DateTimeInterface
     {
         return $this->date;
     }


Test patch:
diff --git a/tests/OpeningHoursOverflowTest.php b/tests/OpeningHoursOverflowTest.php
index 1a9aebb..530cb59 100644
--- a/tests/OpeningHoursOverflowTest.php
+++ b/tests/OpeningHoursOverflowTest.php
@@ -177,4 +177,48 @@ class OpeningHoursOverflowTest extends TestCase
         $this->assertFalse($openWithOverflow->isOpenAt($time));
         $this->assertFalse($openWithoutOverflow->isOpenAt($time));
     }
+
+    #[Test]
+    public function overflow_next_close()
+    {
+        $openingHours = OpeningHours::create([
+            'overflow' => true,
+            'monday' => ['18:00-05:00'], // 2024-11-11
+            'tuesday' => ['17:00-06:00'], // 2024-11-12
+        ]);
+
+        $nextClose = $openingHours->nextClose(new DateTime('2024-11-12 04:00:00'));
+
+        $this->assertSame('2024-11-12 05:00', $nextClose->format('Y-m-d H:i'));
+
+        $nextClose = $openingHours->nextClose(new DateTime('2024-11-12 05:30:00'));
+
+        $this->assertSame('2024-11-13 06:00', $nextClose->format('Y-m-d H:i'));
+
+        $nextClose = $openingHours->nextClose(new DateTime('2024-11-12 05:30:00'));
+
+        $openingHours = OpeningHours::create([
+            'overflow' => true,
+            'monday' => ['18:00-05:00'], // 2024-11-11
+            'tuesday' => ['05:40-05:50', '17:00-06:00'], // 2024-11-12
+        ]);
+
+        $nextClose = $openingHours->nextClose(new DateTime('2024-11-12 05:30:00'));
+
+        $this->assertSame('2024-11-12 05:50', $nextClose->format('Y-m-d H:i'));
+
+        $openingHours = OpeningHours::create([
+            'overflow' => true,
+            'monday' => ['18:00-22:00', '23:00-05:00'], // 2024-11-11
+            'tuesday' => ['17:00-06:00'], // 2024-11-12
+        ]);
+
+        $nextClose = $openingHours->nextClose(new DateTime('2024-11-11 23:30:00'));
+
+        $this->assertSame('2024-11-12 05:00', $nextClose->format('Y-m-d H:i'));
+
+        $nextClose = $openingHours->nextClose(new DateTime('2024-11-12 04:00:00'));
+
+        $this->assertSame('2024-11-12 05:00', $nextClose->format('Y-m-d H:i'));
+    }
 }