nl-x
nl-x

Reputation: 11832

Is strtotime() having a bug?

Consider this code where we want to add or substract one second:

date_default_timezone_set("Europe/Amsterdam");

$time = 1477789199;
echo $time . '  -  ' . date('r', $time) . "\n";
// 1477789199  -  Sun, 30 Oct 2016 02:59:59 +0200

This is correct, as this timestamp is still just within DST (daylight savings time / summer time).

But now let's add one second to the timestamp integer, and exit DST:

$new = $time + 1;
echo $new . '  -  ' . date('r', $new);
// 1477789200  -  Sun, 30 Oct 2016 02:00:00 +0100

Hooray! PHP sees that one second later there is no DST anymore and shows a proper time string.

But what if we didn't add a second to the timestamp integer, but we used strtotime() to add that one second:

$new = strtotime('+1 second', $time);
echo $new . '  -  ' . date('r', $new);
// 1477792800  -  Sun, 30 Oct 2016 03:00:00 +0100

Yikes! We just went ahead by more than one hour instead of one second. And it doesn't even matter if you add one second, one hour, one day or one year you will always get one extra hour with it. Even if you add multiple years, you will only get one extra hour, which is weird because we enter and exit DST every year but you only get one extra hour regardless of how many years you add

But once we exit DST in October and subtract one second, all goes fine...

But then again. If we were in March and we have just entered DST, and we subtract one second, we observe exactly the same in reverse.


Wait, what ?! So ... ?

echo strtotime('+ 1 second - 1 second', 1477789199); // echoes 1477792799

Whoa ...


To me this sounds like a bug. Or is this 'by design'? Does anyone even know if this is documented somewhere, or whether it needs to be reported?

Upvotes: 4

Views: 361

Answers (1)

bwoebi
bwoebi

Reputation: 23777

The behavior is "well documented" .... in a test:

See https://bugs.php.net/bug.php?id=30532 (which also presents your expected result as expected) and the related test file (which asserts that the current behavior is correct) https://github.com/php/php-src/blob/master/ext/date/tests/bug30532.phpt

<?php date_default_timezone_set("America/New_York");

echo date('Y-m-d H:i:s T', strtotime('2004-10-31 EDT +1 hour'))."\n";
echo date('Y-m-d H:i:s T', strtotime('2004-10-31 EDT +2 hours'))."\n";
echo date('Y-m-d H:i:s T', strtotime('2004-10-31 EDT +3 hours'))."\n";
/* 2004-10-31 01:00:00 EDT
   2004-10-31 01:00:00 EST
   2004-10-31 02:00:00 EST */

echo date('Y-m-d H:i:s T', strtotime('2004-10-31 +1 hour'))."\n";
echo date('Y-m-d H:i:s T', strtotime('2004-10-31 +2 hours'))."\n";
echo date('Y-m-d H:i:s T', strtotime('2004-10-31 +3 hours'))."\n";
/* 2004-10-31 01:00:00 EDT
   2004-10-31 02:00:00 EST
   2004-10-31 03:00:00 EST */

Note that in the former case the timezone (here: EDT) is being passed directly to the string, in the latter case it isn't.

In general strtotime is taking the timestamp (i.e. of 2004-10-31 - or in your specific case: the passed timestamp), converted to a representation with individual parameters, ignoring DST (i.e. individual hours, minutes, seconds, day, month, year etc.), the operation is applied to it and then converted back to the timestamp.

In particular:

echo date('r', strtotime('+ 0 second', 1477789199));
#> Sun, 30 Oct 2016 02:59:59 +0100

strtotime() throws the timezone after conversion away, i.e. only takes

Sun, 30 Oct 2016 02:59:59

and then applies the primary applicable timezone to your timezone location (i.e. Europe/Amsterdam), ending up with CET (primary!) - CEST is also possible, but only second choice.

Now, look back at the test above, just specify the originating timezone explicitly.

Thus, if you wish it to behave the way you need it:

echo date('r', strtotime('CEST', 1477789199));
#> Sun, 30 Oct 2016 02:59:59 +0200
echo date('r', strtotime('CEST + 1 second', 1477789199));
#> Sun, 30 Oct 2016 02:00:00 +0100

In fact, prepending 'CEST ' will be fine for all the times (as it will always fallback to CET if CEST is not matching and there's no overlap on CET -> CEST transition).

Upvotes: 1

Related Questions