4

Is a DateTime object not bound by its timestamp? Or does getTimestamp() has some kind of side-effect when used on DST change?

Details
When setting the timestamp of a DateTime object which is on DST (meaning the formatted time exists both before/after changing the clock) the returned timestamp differs from the set timestamp.

$ php --version
PHP 7.1.3 (cli) (built: Mar 17 2017 16:59:59) ( NTS )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.1.0, Copyright (c) 1998-2017 Zend Technologies

Reproduce
Consider the following php script:

date_default_timezone_set('Europe/Berlin');

$date = new DateTime();
$set_timestamp = 1319932800;
$date->setTimestamp($set_timestamp);
$get_timestamp = $date->getTimestamp();

fwrite(STDERR, $set_timestamp . "\n");  // 1319932800
fwrite(STDERR, $get_timestamp . "\n");  // 1319936400 **(WHY IS THIS DIFFERENT?)**

Why are the printed values not equal?

p1100i
  • 3,710
  • 2
  • 29
  • 45
  • @elbarto ofc you get the same. My problem relies explicitly around the DST handling of PHP about DateTime and for DST change for the given timestamp you need `Europe/Berlin` or similar. I can easily generate a timestamp for u (which will fail simirarly), if u give us your default timezone – p1100i Apr 05 '17 at 07:55
  • Please check if it is already there https://bugs.php.net/search.php?cmd=display&order_by=id&direction=DESC&limit=30&package_name%5B%5D=Date%2Ftime+related. – Alex Blex Apr 05 '17 at 07:56
  • i think it is because the timezone doesn't apply to the static value $set_timestamp however the timezone does apply $get_timestamp because its value was generated, thats what i think but either way its interesting will give it a try later my self – Gert Apr 05 '17 at 08:10

2 Answers2

3

\DateTime object doesn't keep timestamps but local time, and do conversions in timestamp getter and timestamp setter.

It results with a side-effect once a year when DST is being switched off, since both timestamp ranges 1319932800..1319936400 and 1319936400..1319940000 resolve to the same local time:
https://www.epochconverter.com/timezones?q=1319936399&tz=Europe%2FBerlin https://www.epochconverter.com/timezones?q=1319939999&tz=Europe%2FBerlin

Alex Blex
  • 34,704
  • 7
  • 48
  • 75
  • I see, so basically, the `getter/setter` has side effects? – p1100i Apr 05 '17 at 08:55
  • and also datetime object is not bound by its timestamp, rather its bounded by the local-time + timestamp together (in-a-weird php-kind-of-way)? – p1100i Apr 05 '17 at 09:01
  • no, it's local-time+timezone only, which results with a spike of similar questions on SO twice a year. – Alex Blex Apr 05 '17 at 09:09
3

First of all, the unix timestamp is always in UTC, so it hasn't timezone and DST.

In other hand, the DateTime object stores local time only (the "local" means what timezone is set in the DateTime instance).

Therefore you should set timezone to +00:00 or UTC before you set a timestamp for avoid unnecessary time conversions and DST guessing.

You have two choices:

1. Set timestamp via constructor of DateTime

The constructor will overrides the default timezone and explicit set to +00:00 when it got timestamp (started with @) in first parameter:

$set_timestamp = 1319932800;
$date = new DateTime('@' . $set_timestamp);

print($set_timestamp . "\n");
print($date->getTimestamp() . "\n");

Info: in this case, the timezone parameter of constructor always will be overridden.

2. Set timezone before call setTimestamp()

Call setTimezone() with DateTimeZone('+00:00') or DateTimeZone('UTC') timezone before you call setTimestamp():

$set_timestamp = 1319932800;
$date = new DateTime();
$date->setTimezone(new DateTimeZone('UTC'));
$date->setTimestamp($set_timestamp);

print($set_timestamp . "\n");
print($date->getTimestamp() . "\n");

Notes

Of course, both of these cases the output will be:

1319932800
1319932800

The date_default_timezone_set() is unnecessary in these cases, because you don't want to do anything with local time.

However when you want to print the $date in human readable format (so when you convert the unix timestamp to local time) the timezone will be interesting again.

andras.tim
  • 1,964
  • 1
  • 13
  • 23
  • It may be worth to mention that class `DateTime` accept object of `DateTimeZone` in constructor as second parameter – SzymonM Apr 05 '17 at 14:12
  • Not working if timezone is changed back to `Europe/Berlin` before `getTimestamp()` https://repl.it/GwOF/0 =( Assuming @p1100i needs local timezone for other datetime calculations. – Alex Blex Apr 05 '17 at 15:04