From 9cf14e338a1cc655bec6b9afdc1e6de1725d7d8d Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 26 Mar 2024 12:03:52 +0900 Subject: [PATCH 1/5] fix: Time::difference() DST bug Fixed to not convert to UTC when the two time zones are the same. --- system/I18n/TimeTrait.php | 17 +++++++++++++++-- tests/system/I18n/TimeDifferenceTest.php | 20 ++++++++++++++------ 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/system/I18n/TimeTrait.php b/system/I18n/TimeTrait.php index a2a92b8fc94d..5476f4c049ed 100644 --- a/system/I18n/TimeTrait.php +++ b/system/I18n/TimeTrait.php @@ -1069,8 +1069,21 @@ public function humanize() */ public function difference($testTime, ?string $timezone = null) { - $testTime = $this->getUTCObject($testTime, $timezone); - $ourTime = $this->getUTCObject($this); + if (is_string($testTime)) { + $timezone = ($timezone !== null) ? new DateTimeZone($timezone) : $this->timezone; + $testTime = new DateTime($testTime, $timezone); + } elseif ($testTime instanceof self) { + $testTime = $testTime->toDateTime(); + } + + assert($testTime instanceof DateTime); + + if ($this->timezone->getOffset($this) !== $testTime->getTimezone()->getOffset($this)) { + $testTime = $this->getUTCObject($testTime, $timezone); + $ourTime = $this->getUTCObject($this); + } else { + $ourTime = $this->toDateTime(); + } return new TimeDifference($ourTime, $testTime); } diff --git a/tests/system/I18n/TimeDifferenceTest.php b/tests/system/I18n/TimeDifferenceTest.php index 0d88e68cf40f..2867e3017c94 100644 --- a/tests/system/I18n/TimeDifferenceTest.php +++ b/tests/system/I18n/TimeDifferenceTest.php @@ -120,7 +120,7 @@ public function testHumanizeMonthsForward(): void $current = Time::parse('March 1, 2017', 'America/Chicago'); $diff = $current->difference('May 1, 2017', 'America/Chicago'); - $this->assertSame('in 1 month', $diff->humanize('en')); + $this->assertSame('in 2 months', $diff->humanize('en')); } public function testHumanizeDaysSingle(): void @@ -211,12 +211,20 @@ public function testHumanizeWeeksPlural(): void $this->assertSame('2 weeks ago', $diff->humanize('en')); } - public function testHumanizeWeeksForward(): void + public function testHumanizeWeeksForwardDST(): void { $current = Time::parse('March 10, 2017', 'America/Chicago'); $diff = $current->difference('March 18, 2017', 'America/Chicago'); - $this->assertSame('in 1 week', $diff->humanize('en')); + $this->assertSame('in 2 weeks', $diff->humanize('en')); + } + + public function testHumanizeWeeksForwardUTC(): void + { + $current = Time::parse('2017-03-10'); + $diff = $current->difference('2017-03-18'); + + $this->assertSame('in 2 weeks', $diff->humanize('en')); } public function testHumanizeNoDifference(): void @@ -238,14 +246,14 @@ public function testGetterUTC(): void $this->assertNull($diff->nonsense); } - public function testGetterChicagoTime(): void + public function testGetterDST(): void { $current = Time::parse('March 10, 2017', 'America/Chicago'); $diff = $current->difference('March 18, 2017', 'America/Chicago'); // Daylight Saving Time had begun since Sun, 12 Mar, 02:00. - $this->assertSame(7, $diff->getDays()); - $this->assertSame(7, $diff->days); + $this->assertSame(8, $diff->getDays()); + $this->assertSame(8, $diff->days); // The raw value does not take Daylight Saving Time into account. $this->assertSame(-8, (int) round($diff->getDays(true))); From bb201e674dc80d0c832904c91c19a2e5e3f0830c Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 26 Mar 2024 09:05:51 +0900 Subject: [PATCH 2/5] docs: add empty line --- user_guide_src/source/libraries/time.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/user_guide_src/source/libraries/time.rst b/user_guide_src/source/libraries/time.rst index 668a7bebbe7c..c58755736e11 100644 --- a/user_guide_src/source/libraries/time.rst +++ b/user_guide_src/source/libraries/time.rst @@ -366,7 +366,9 @@ Viewing Differences =================== To compare two Times directly, you would use the ``difference()`` method, which returns a ``CodeIgniter\I18n\TimeDifference`` -instance. The first parameter is either a Time instance, a DateTime instance, or a string with the date/time. If +instance. + +The first parameter is either a Time instance, a DateTime instance, or a string with the date/time. If a string is passed in the first parameter, the second parameter can be a timezone string: .. literalinclude:: time/038.php From 8a8a67c1c70a25a19fb6c3c62be8cb10d73447c3 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 26 Mar 2024 09:19:18 +0900 Subject: [PATCH 3/5] docs: add note for DST and Time::difference() --- user_guide_src/source/libraries/time.rst | 9 +++++++++ user_guide_src/source/libraries/time/042.php | 12 ++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 user_guide_src/source/libraries/time/042.php diff --git a/user_guide_src/source/libraries/time.rst b/user_guide_src/source/libraries/time.rst index c58755736e11..47a91b1d3a92 100644 --- a/user_guide_src/source/libraries/time.rst +++ b/user_guide_src/source/libraries/time.rst @@ -379,6 +379,15 @@ the original time: .. literalinclude:: time/039.php +.. note:: Prior to v4.4.7, Time always converted the time zones to UTC before + comparison. This could lead to unexpected results when containing a day + differed from 24 hours due to Daylight Saving Time (DST). + + Starting with v4.4.7, when comparing date/times that are in the same + time zone, the comparison is performed as is, without conversion to UTC. + + .. literalinclude:: time/042.php + You can use either ``getX()`` methods, or access the calculate values as if they were properties: .. literalinclude:: time/040.php diff --git a/user_guide_src/source/libraries/time/042.php b/user_guide_src/source/libraries/time/042.php new file mode 100644 index 000000000000..f29b3af4323e --- /dev/null +++ b/user_guide_src/source/libraries/time/042.php @@ -0,0 +1,12 @@ +difference($test); + +echo $diff->getDays(); // 0 in v4.4.6 or before + // 1 in v4.4.7 or later From 6ae5743420a428ad7684c25d1731e259eb637da7 Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 27 Mar 2024 21:49:05 +0900 Subject: [PATCH 4/5] docs: add changelog and upgrade --- user_guide_src/source/changelogs/v4.4.7.rst | 5 +++++ .../source/installation/upgrade_447.rst | 15 +++++++++++++++ user_guide_src/source/libraries/time.rst | 2 ++ 3 files changed, 22 insertions(+) diff --git a/user_guide_src/source/changelogs/v4.4.7.rst b/user_guide_src/source/changelogs/v4.4.7.rst index c7c4bd3fa382..93193d0a1cc9 100644 --- a/user_guide_src/source/changelogs/v4.4.7.rst +++ b/user_guide_src/source/changelogs/v4.4.7.rst @@ -14,6 +14,11 @@ Release Date: Unreleased BREAKING ******** +- In previous versions, when comparing dates with ``Time::difference()``, + unexpected results were returned if the date included a day different from 24 + hours due to Daylight Saving Time (DST). This bug has been fixed. See + :ref:`Note in Times and Dates ` for details. + *************** Message Changes *************** diff --git a/user_guide_src/source/installation/upgrade_447.rst b/user_guide_src/source/installation/upgrade_447.rst index 45eed909332e..dbb679a3ad2d 100644 --- a/user_guide_src/source/installation/upgrade_447.rst +++ b/user_guide_src/source/installation/upgrade_447.rst @@ -28,6 +28,21 @@ The error page has been updated. Please update the following files: Breaking Changes **************** +Time::difference() and DST +========================== + +In previous versions, when comparing dates with ``Time::difference()``, unexpected +results were returned if the date included a day different from 24 hours due to +Daylight Saving Time (DST). See :ref:`Note in Times and Dates ` +for details. + +This bug has been fixed, so date comparisons will now be shifted by one day in +such cases. + +In the unlikely event that you wish to maintain the behavior of the previous +versions, change the time zone of both dates being compared to UTC before passing +them to ``Time::difference()``. + ********************* Breaking Enhancements ********************* diff --git a/user_guide_src/source/libraries/time.rst b/user_guide_src/source/libraries/time.rst index 47a91b1d3a92..58388250d22b 100644 --- a/user_guide_src/source/libraries/time.rst +++ b/user_guide_src/source/libraries/time.rst @@ -362,6 +362,8 @@ Works exactly the same as ``isBefore()`` except checks if the time is after the .. literalinclude:: time/037.php +.. _time-viewing-differences: + Viewing Differences =================== From f99ca6afae041de53d1247bf544433c2a9334f74 Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 27 Mar 2024 21:51:34 +0900 Subject: [PATCH 5/5] docs: fix coding style --- user_guide_src/source/libraries/time/042.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/user_guide_src/source/libraries/time/042.php b/user_guide_src/source/libraries/time/042.php index f29b3af4323e..6f2c77498e6f 100644 --- a/user_guide_src/source/libraries/time/042.php +++ b/user_guide_src/source/libraries/time/042.php @@ -8,5 +8,6 @@ $diff = $current->difference($test); -echo $diff->getDays(); // 0 in v4.4.6 or before - // 1 in v4.4.7 or later +echo $diff->getDays(); +// 0 in v4.4.6 or before +// 1 in v4.4.7 or later