Bug #81072 [Opn->Fbk]: Incorrect behavior of DateTime->modify across DST changes

From: Date: Sat, 22 May 2021 20:17:54 +0000
Subject: Bug #81072 [Opn->Fbk]: Incorrect behavior of DateTime->modify across DST changes
References: 1  Groups: php.bugs 
Request: Send a blank email to php-bugs+get-233980@lists.php.net to get a copy of this message
Edit report at https://bugs.php.net/bug.php?id=81072&edit=1

 ID:                 81072
 Updated by:         requinix@php.net
 Reported by:        ph-bugs at allenjb dot me dot uk
 Summary:            Incorrect behavior of DateTime->modify across DST
                     changes
-Status:             Open
+Status:             Feedback
 Type:               Bug
 Package:            Date/time related
 PHP Version:        8.0.6
 Block user comment: N
 Private report:     N

 New Comment:

Looks like 3v4l.org is behind a few versions - maybe was impacted by the GitHub move.

I can't reproduce locally. Can you test in current master (8.1)? If it works there then this
may be a duplicate of 74274.


Previous Comments:
------------------------------------------------------------------------
[2021-05-22 18:55:52] ph-bugs at allenjb dot me dot uk

Description:
------------
See https://3v4l.org/hGXcr

Working on a fix for https://github.com/dragonmantank/cron-expression/issues/111

As you can see, when working backwards with ->modify("-1 hour") DateTime fails to
properly handle the DST change and keeps pushing the time forward to 03:00:00

Working forwards with ->modify("+1 hour") does not exhibit the same behavior.

(The code examples also show a workaround, which involved switching timezones while performing the
->modify)

Test script:
---------------
<?php

function incrementWithSwitch($date, $invert)
{
    $timezone = $date->getTimezone();
    $date = $date->setTimezone(new DateTimeZone('UTC'));
    $date = $date->modify(($invert ? '-' : '+') . '1 hour');
    $date = $date->setTimezone($timezone);

    $date = $date->setTime((int)$date->format('H'), 0);
    return $date;
}


function incrementWithoutSwitch($date, $invert)
{
    $date = $date->modify(($invert ? '-' : '+') . '1 hour');
    return $date;
}

$tz = new \DateTimeZone("Europe/London");
$dtWith = \DateTimeImmutable::createFromFormat("!Y-m-d H:i:s", "2021-03-28
04:00:00");
$dtWithout = $dtWith;

$invert = true;
for ($i = 0; $i < 3; $i++) {
    print "\nRound {$i}:";
    $dtWith = incrementWithSwitch($dtWith, $invert);
    $dtWithout = incrementWithoutSwitch($dtWithout, $invert);
    print "With switch:    ". $dtWith->format(\DateTime::RFC3339);
    print " :: Without switch: ". $dtWithout->format(\DateTime::RFC3339);
}

print "\n---\n";

$dtWith = \DateTimeImmutable::createFromFormat("!Y-m-d H:i:s", "2021-03-28
00:00:00");
$dtWithout = $dtWith;

$invert = false;
for ($i = 0; $i < 3; $i++) {
    print "\nRound {$i}:";
    $dtWith = incrementWithSwitch($dtWith, $invert);
    $dtWithout = incrementWithoutSwitch($dtWithout, $invert);
    print "With switch:    ". $dtWith->format(\DateTime::RFC3339);
    print " :: Without switch: ". $dtWithout->format(\DateTime::RFC3339);
}

Expected result:
----------------
Round 0:With switch:    2021-03-28T03:00:00+02:00 :: Without switch: 2021-03-28T03:00:00+02:00
Round 1:With switch:    2021-03-28T01:00:00+01:00 :: Without switch: 2021-03-28T01:00:00+02:00
Round 2:With switch:    2021-03-28T00:00:00+01:00 :: Without switch: 2021-03-28T00:00:00+02:00
---

Round 0:With switch:    2021-03-28T01:00:00+01:00 :: Without switch: 2021-03-28T01:00:00+01:00
Round 1:With switch:    2021-03-28T03:00:00+02:00 :: Without switch: 2021-03-28T03:00:00+02:00
Round 2:With switch:    2021-03-28T04:00:00+02:00 :: Without switch: 2021-03-28T04:00:00+02:00

Actual result:
--------------
Round 0:With switch:    2021-03-28T03:00:00+02:00 :: Without switch: 2021-03-28T03:00:00+02:00
Round 1:With switch:    2021-03-28T01:00:00+01:00 :: Without switch: 2021-03-28T03:00:00+02:00
Round 2:With switch:    2021-03-28T00:00:00+01:00 :: Without switch: 2021-03-28T03:00:00+02:00
---

Round 0:With switch:    2021-03-28T01:00:00+01:00 :: Without switch: 2021-03-28T01:00:00+01:00
Round 1:With switch:    2021-03-28T03:00:00+02:00 :: Without switch: 2021-03-28T03:00:00+02:00
Round 2:With switch:    2021-03-28T04:00:00+02:00 :: Without switch: 2021-03-28T04:00:00+02:00


------------------------------------------------------------------------



--
Edit this bug report at https://bugs.php.net/bug.php?id=81072&edit=1


Thread (11 messages)

« previous php.bugs (#233980) next »