Today encountered interesting feature (?) in PHP DateTime::setTimestamp()
behavior. During winter DST changes, when 1 hour repeats twice, PHP converts timestamp to always be the second hour. Consider following example:
<?php
$date = new \DateTime("now", new \DateTimeZone("UTC"));
//2018-10-28T01:30:00 UTC, in London DST happens
$date->setTimestamp(1540686600);
echo $date->getTimestamp() . "\n";
//1540686600
echo $date->format('c') . "\n";
//2018-10-28T00:30:00+00:00
$date->setTimezone(new \DateTimeZone("Europe/London"));
echo $date->getTimestamp() . "\n";
//1540690200
$date->setTimezone(new \DateTimeZone("UTC"));
echo $date->getTimestamp() . "\n";
//1540690200
echo $date->format('c') . "\n";
//2018-10-28T01:30:00+00:00
Looking at source PHP tries to convert timestamp to local time (o_O). So two questions:
- why there's conversion of timestamp? Timestamp is always in UTC and should be local time/timezone agnostic
- is there any simple solution to keep both timezone id and intended timestamp in
DateTime
object? (I am aware of possibility to keep everything inUTC
and convert to local timezone only when showing)
Found possible bug filed in PHP
UPD 2016-01-04 (in UTC):
Narrowed down problem to DateTime::getTimestamp()
. Consider following code:
<?php
$date = new \DateTime("now", new \DateTimeZone("UTC"));
//2018-10-28T00:30:00 UTC
$date->setTimestamp(1540686600);
echo $date->format('c') . "\n"; //2018-10-28T00:30:00+00:00
$date->setTimezone(new \DateTimeZone("Europe/London"));
echo $date->format('c') . "\n"; //2018-10-28T01:30:00+01:00
$date->setTimezone(new \DateTimeZone("UTC"));
echo $date->format('c') . "\n"; //2018-10-28T00:30:00+00:00
echo $date->getTimestamp() . "\n"; //1540686600
Here timestamp is not altered and code works as expected. Following code that is similar and was given in original example is broken:
<?php
$date = new \DateTime("now", new \DateTimeZone("UTC"));
//2018-10-28T00:30:00 UTC
$date->setTimestamp(1540686600);
echo $date->format('c') . "\n"; //2018-10-28T00:30:00+00:00
$date->setTimezone(new \DateTimeZone("Europe/London"));
echo $date->format('c') . "\n"; //2018-10-28T01:30:00+01:00
//-------------- the only line that was added ------------------
echo $date->getTimestamp() . "\n"; //1540690200
//-------------- end of added line -----------------------------
$date->setTimezone(new \DateTimeZone("UTC"));
echo $date->format('c') . "\n"; //2018-10-28T01:30:00+00:00
echo $date->getTimestamp() . "\n"; //1540690200