Skip to content

Commit 8e64913

Browse files
authored
Merge pull request #8661 from kenjis/fix-time-diff-DST
fix: Time::difference() DST bug
2 parents 87652c9 + f99ca6a commit 8e64913

File tree

6 files changed

+76
-9
lines changed

6 files changed

+76
-9
lines changed

system/I18n/TimeTrait.php

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1069,8 +1069,21 @@ public function humanize()
10691069
*/
10701070
public function difference($testTime, ?string $timezone = null)
10711071
{
1072-
$testTime = $this->getUTCObject($testTime, $timezone);
1073-
$ourTime = $this->getUTCObject($this);
1072+
if (is_string($testTime)) {
1073+
$timezone = ($timezone !== null) ? new DateTimeZone($timezone) : $this->timezone;
1074+
$testTime = new DateTime($testTime, $timezone);
1075+
} elseif ($testTime instanceof self) {
1076+
$testTime = $testTime->toDateTime();
1077+
}
1078+
1079+
assert($testTime instanceof DateTime);
1080+
1081+
if ($this->timezone->getOffset($this) !== $testTime->getTimezone()->getOffset($this)) {
1082+
$testTime = $this->getUTCObject($testTime, $timezone);
1083+
$ourTime = $this->getUTCObject($this);
1084+
} else {
1085+
$ourTime = $this->toDateTime();
1086+
}
10741087

10751088
return new TimeDifference($ourTime, $testTime);
10761089
}

tests/system/I18n/TimeDifferenceTest.php

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ public function testHumanizeMonthsForward(): void
120120
$current = Time::parse('March 1, 2017', 'America/Chicago');
121121
$diff = $current->difference('May 1, 2017', 'America/Chicago');
122122

123-
$this->assertSame('in 1 month', $diff->humanize('en'));
123+
$this->assertSame('in 2 months', $diff->humanize('en'));
124124
}
125125

126126
public function testHumanizeDaysSingle(): void
@@ -211,12 +211,20 @@ public function testHumanizeWeeksPlural(): void
211211
$this->assertSame('2 weeks ago', $diff->humanize('en'));
212212
}
213213

214-
public function testHumanizeWeeksForward(): void
214+
public function testHumanizeWeeksForwardDST(): void
215215
{
216216
$current = Time::parse('March 10, 2017', 'America/Chicago');
217217
$diff = $current->difference('March 18, 2017', 'America/Chicago');
218218

219-
$this->assertSame('in 1 week', $diff->humanize('en'));
219+
$this->assertSame('in 2 weeks', $diff->humanize('en'));
220+
}
221+
222+
public function testHumanizeWeeksForwardUTC(): void
223+
{
224+
$current = Time::parse('2017-03-10');
225+
$diff = $current->difference('2017-03-18');
226+
227+
$this->assertSame('in 2 weeks', $diff->humanize('en'));
220228
}
221229

222230
public function testHumanizeNoDifference(): void
@@ -238,14 +246,14 @@ public function testGetterUTC(): void
238246
$this->assertNull($diff->nonsense);
239247
}
240248

241-
public function testGetterChicagoTime(): void
249+
public function testGetterDST(): void
242250
{
243251
$current = Time::parse('March 10, 2017', 'America/Chicago');
244252
$diff = $current->difference('March 18, 2017', 'America/Chicago');
245253

246254
// Daylight Saving Time had begun since Sun, 12 Mar, 02:00.
247-
$this->assertSame(7, $diff->getDays());
248-
$this->assertSame(7, $diff->days);
255+
$this->assertSame(8, $diff->getDays());
256+
$this->assertSame(8, $diff->days);
249257

250258
// The raw value does not take Daylight Saving Time into account.
251259
$this->assertSame(-8, (int) round($diff->getDays(true)));

user_guide_src/source/changelogs/v4.4.7.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ Release Date: Unreleased
1414
BREAKING
1515
********
1616

17+
- In previous versions, when comparing dates with ``Time::difference()``,
18+
unexpected results were returned if the date included a day different from 24
19+
hours due to Daylight Saving Time (DST). This bug has been fixed. See
20+
:ref:`Note in Times and Dates <time-viewing-differences>` for details.
21+
1722
***************
1823
Message Changes
1924
***************

user_guide_src/source/installation/upgrade_447.rst

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,21 @@ The error page has been updated. Please update the following files:
2828
Breaking Changes
2929
****************
3030

31+
Time::difference() and DST
32+
==========================
33+
34+
In previous versions, when comparing dates with ``Time::difference()``, unexpected
35+
results were returned if the date included a day different from 24 hours due to
36+
Daylight Saving Time (DST). See :ref:`Note in Times and Dates <time-viewing-differences>`
37+
for details.
38+
39+
This bug has been fixed, so date comparisons will now be shifted by one day in
40+
such cases.
41+
42+
In the unlikely event that you wish to maintain the behavior of the previous
43+
versions, change the time zone of both dates being compared to UTC before passing
44+
them to ``Time::difference()``.
45+
3146
*********************
3247
Breaking Enhancements
3348
*********************

user_guide_src/source/libraries/time.rst

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,11 +365,15 @@ Works exactly the same as ``isBefore()`` except checks if the time is after the
365365

366366
.. literalinclude:: time/037.php
367367

368+
.. _time-viewing-differences:
369+
368370
Viewing Differences
369371
===================
370372

371373
To compare two Times directly, you would use the ``difference()`` method, which returns a ``CodeIgniter\I18n\TimeDifference``
372-
instance. The first parameter is either a Time instance, a DateTime instance, or a string with the date/time. If
374+
instance.
375+
376+
The first parameter is either a Time instance, a DateTime instance, or a string with the date/time. If
373377
a string is passed in the first parameter, the second parameter can be a timezone string:
374378

375379
.. literalinclude:: time/038.php
@@ -380,6 +384,15 @@ the original time:
380384

381385
.. literalinclude:: time/039.php
382386

387+
.. note:: Prior to v4.4.7, Time always converted the time zones to UTC before
388+
comparison. This could lead to unexpected results when containing a day
389+
differed from 24 hours due to Daylight Saving Time (DST).
390+
391+
Starting with v4.4.7, when comparing date/times that are in the same
392+
time zone, the comparison is performed as is, without conversion to UTC.
393+
394+
.. literalinclude:: time/042.php
395+
383396
You can use either ``getX()`` methods, or access the calculate values as if they were properties:
384397

385398
.. literalinclude:: time/040.php
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
use CodeIgniter\I18n\Time;
4+
5+
// 31 Mar 2024 - Daylight Saving Time Starts
6+
$current = Time::parse('2024-03-31', 'Europe/Madrid');
7+
$test = Time::parse('2024-04-01', 'Europe/Madrid');
8+
9+
$diff = $current->difference($test);
10+
11+
echo $diff->getDays();
12+
// 0 in v4.4.6 or before
13+
// 1 in v4.4.7 or later

0 commit comments

Comments
 (0)