Throw exception on invalid timezone ID
Work around PHP 8.1 regression
Showing 2 of 3 files from the diff.
src/Carbon/Traits/Test.php
changed.
src/Carbon/Traits/Difference.php
changed.
Other files ignored by Codecov
tests/Carbon/TestingAidsTest.php
has changed.
@@ -12,9 +12,12 @@
Loading
12 | 12 | namespace Carbon\Traits; |
|
13 | 13 | ||
14 | 14 | use Carbon\CarbonInterface; |
|
15 | + | use Carbon\CarbonTimeZone; |
|
15 | 16 | use Closure; |
|
16 | 17 | use DateTimeImmutable; |
|
17 | 18 | use DateTimeInterface; |
|
19 | + | use InvalidArgumentException; |
|
20 | + | use Throwable; |
|
18 | 21 | ||
19 | 22 | trait Test |
|
20 | 23 | { |
@@ -84,13 +87,14 @@
Loading
84 | 87 | $useDateInstanceTimezone = $testNow instanceof DateTimeInterface; |
|
85 | 88 | ||
86 | 89 | if ($useDateInstanceTimezone) { |
|
87 | - | date_default_timezone_set($testNow->getTimezone()->getName()); |
|
90 | + | self::setDefaultTimezone($testNow->getTimezone()->getName(), $testNow); |
|
88 | 91 | } |
|
89 | 92 | ||
90 | 93 | static::setTestNow($testNow); |
|
91 | 94 | ||
92 | 95 | if (!$useDateInstanceTimezone) { |
|
93 | - | date_default_timezone_set(static::getMockedTestNow(\func_num_args() === 1 ? null : $tz)->timezone); |
|
96 | + | $now = static::getMockedTestNow(\func_num_args() === 1 ? null : $tz); |
|
97 | + | self::setDefaultTimezone($now->tzName, $now); |
|
94 | 98 | } |
|
95 | 99 | } |
|
96 | 100 |
@@ -175,4 +179,30 @@
Loading
175 | 179 | ? $testInstance->rawFormat(static::MOCK_DATETIME_FORMAT) |
|
176 | 180 | : $testInstance->format(static::MOCK_DATETIME_FORMAT); |
|
177 | 181 | } |
|
182 | + | ||
183 | + | private static function setDefaultTimezone($timezone, DateTimeInterface $date = null) |
|
184 | + | { |
|
185 | + | $previous = null; |
|
186 | + | $success = false; |
|
187 | + | ||
188 | + | try { |
|
189 | + | $success = date_default_timezone_set($timezone); |
|
190 | + | } catch (Throwable $exception) { |
|
191 | + | $previous = $exception; |
|
192 | + | } |
|
193 | + | ||
194 | + | if (!$success) { |
|
195 | + | $suggestion = @CarbonTimeZone::create($timezone)->toRegionName($date); |
|
196 | + | ||
197 | + | throw new InvalidArgumentException( |
|
198 | + | "Timezone ID '$timezone' is invalid". |
|
199 | + | ($suggestion && $suggestion !== $timezone ? ", did you mean '$suggestion'?" : '.')."\n". |
|
200 | + | "It must be one of the IDs from DateTimeZone::listIdentifiers(),\n". |
|
201 | + | 'For the record, hours/minutes offset are relevant only for a particular moment, '. |
|
202 | + | 'but not as a default timezone.', |
|
203 | + | 0, |
|
204 | + | $previous |
|
205 | + | ); |
|
206 | + | } |
|
207 | + | } |
|
178 | 208 | } |
@@ -217,7 +217,7 @@
Loading
217 | 217 | */ |
|
218 | 218 | public function diffInDays($date = null, $absolute = true) |
|
219 | 219 | { |
|
220 | - | return (int) $this->diff($this->resolveCarbon($date), $absolute)->format('%r%a'); |
|
220 | + | return $this->getIntervalDayDiff($this->diff($this->resolveCarbon($date), $absolute)); |
|
221 | 221 | } |
|
222 | 222 | ||
223 | 223 | /** |
@@ -518,7 +518,7 @@
Loading
518 | 518 | return $hoursDiff / static::HOURS_PER_DAY; |
|
519 | 519 | } |
|
520 | 520 | ||
521 | - | $daysDiff = (int) $interval->format('%r%a'); |
|
521 | + | $daysDiff = $this->getIntervalDayDiff($interval); |
|
522 | 522 | ||
523 | 523 | return $daysDiff + fmod($hoursDiff, static::HOURS_PER_DAY) / static::HOURS_PER_DAY; |
|
524 | 524 | } |
@@ -1149,4 +1149,17 @@
Loading
1149 | 1149 | ||
1150 | 1150 | return $this->isoFormat((string) $format); |
|
1151 | 1151 | } |
|
1152 | + | ||
1153 | + | private function getIntervalDayDiff(DateInterval $interval): int |
|
1154 | + | { |
|
1155 | + | $daysDiff = (int) $interval->format('%r%a'); |
|
1156 | + | ||
1157 | + | // Work around PHP 8.1 regression (see https://bugs.php.net/bug.php?id=81458) |
|
1158 | + | if ($daysDiff === 0) { |
|
1159 | + | $daysDiff = ($interval->invert ? -1 : 1) * |
|
1160 | + | ($interval->y * static::DAYS_PER_YEAR + $interval->m * 30 + $interval->d); |
|
1161 | + | } |
|
1162 | + | ||
1163 | + | return $daysDiff; |
|
1164 | + | } |
|
1152 | 1165 | } |
Files | Complexity | Coverage |
---|---|---|
src/Carbon | 2,161 | 100.00% |
Project Totals (886 files) | 2161 | 100.00% |
1474034932
Sunburst
The inner-most circle is the entire project, moving away from the center are folders then, finally, a single file.
The size and color of each slice is representing the number of statements and the coverage, respectively.
Icicle
The top section represents the entire project. Proceeding with folders and finally individual files.
The size and color of each slice is representing the number of statements and the coverage, respectively.