PHP: Subtracting 1 hour from the Dailight Savings Time one hour after the changeover

Viewed 63

I came across a weird thing in PHP. Being in the timezone Europe/Oslo I was subtracting one hour from the timestamp 2020-03-29 03:00:00 (which is exactly when the Dailight Savings Time shift happens) and it resulted in the exact same timestamp 2020-03-29 03:00:00! It should have resulted in 2020-03-29 01:00:00. How come it doesn't?

All the way up to 2020-03-29 03:59:59 it returns the exact same timestamp when subtracting one hour (tried both sub() and modify()). The next second after that it correctly results in 2020-03-29 03:00:00.

You can reproduce it like this:

date_default_timezone_set('Europe/Oslo');
echo (new \DateTime('2020-03-29 03:00:00'))->sub(new \DateInterval('PT1H'))->format('Y-m-d H:i:s');

I have tested this in both PHP 7.1.33, 7.3.10, and 7.3.18.

PS. Adding 1 hour to 2020-03-29 01:00:00 does correctly give 2020-03-29 03:00:00 though.

1 Answers

While I agree that this is bad/buggy behaviour, you can avoid it by strictly only ever storing or computing dates in UTC. Consider time zones to be strictly for display purposes only, and you'll avoid a lot of headaches.

eg:

// input
$utc     = new DateTimeZone('UTC');
$user_tz = new DateTimeZone('Europe/Oslo');
$input   = new DateTime('2020-03-29 03:00:00', $user_tz);
$storage = (clone $input)->setTimezone($utc);
var_dump(
    $input->format('c'),
    $storage->format('c')
);

// compute
$interval = new DateInterval('PT1H');
$storage->sub($interval);
var_dump(
    $storage->format('c')
);

// display
$display = (clone $storage)->setTimezone($user_tz);
var_dump(
    $display->format('c')
);

Output:

string(25) "2020-03-29T03:00:00+02:00"
string(25) "2020-03-29T01:00:00+00:00"
string(25) "2020-03-29T00:00:00+00:00"
string(25) "2020-03-29T01:00:00+01:00"
Related