php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #63442 DateTime::getTimestamp returns wrong value
Submitted: 2012-11-05 16:35 UTC Modified: 2020-11-05 12:56 UTC
Votes:23
Avg. Score:4.4 ± 0.8
Reproduced:22 of 22 (100.0%)
Same Version:5 (22.7%)
Same OS:3 (13.6%)
From: sea128 at post dot cz Assigned: derick (profile)
Status: Not a bug Package: Date/time related
PHP Version: 5.4.8 OS: Windows 7 64 bit
Private report: No CVE-ID: None
 [2012-11-05 16:35 UTC] sea128 at post dot cz
Description:
------------
DateTime::getTimestamp() returns different value from DateTime::setTimestamp().
Problem is caused by time zone where is used daylight saving and time when timezone offset is switched.

Test script:
---------------
$tz = new DateTimeZone('Europe/Prague');
$date = new DateTime('now',$tz);
$date->setTimestamp(1351386000);
echo $date->getTimestamp() . "\n";
$date->setTimestamp(1351386000-3600);
echo $date->getTimestamp() . "\n";

Expected result:
----------------
1351386000
1351382400

Actual result:
--------------
1351386000
1351386000

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2014-09-04 18:47 UTC] mj1856 at hotmail dot com
There's some other strangeness here also.  Consider:

$tz = new DateTimeZone('Europe/Paris');
$dt = new DateTime('2014-10-26T02:30', $tz);
echo $dt->format(DateTime::ISO8601) . " (" . $dt->getTimeStamp() . ")\n";

$ts = $dt->getTimeStamp() - 3600;
$dt = new DateTime("@$ts", new DateTimeZone('UTC'));
$dt->setTimeZone($tz);
echo $dt->format(DateTime::ISO8601) . " (" . $dt->getTimeStamp() . ")\n";

OUTPUT:

2014-10-26T02:30:00+0100 (1414287000)
2014-10-26T02:30:00+0200 (1414287000)

So even though going through UTC fixed the offset shown in the formatting, it still adjusted the timestamp.  The second value should have the timestamp 141283400.

This is a really dangerous bug that has been sitting for too long.  Please raise priority.  Thanks.
 [2015-02-17 14:23 UTC] howard at zedcore dot com
Hello,

I have been able to reproduce this bug when setting DateTime objects to a date and time at the very end of a daylight-savings-time transition.

Daylight-savings (at least in Britain) ends at 02:00:00.

The time goes from 2015-10-25T01:59:59+01:00 to 2015-10-25T01:00:00+00:00.

However, PHP's DateTime objects are broken for the one hour between 01:00:00+01:00 and 01:59:59+01:00. It reports time time offset as +0000 for these times.

I have written a PHPUnit test that reproduces this for both 2014 and 2015. I assume it is the same for every transition.

This does not simply affect use of setTimestamp() and getTimestamp(), the timesone is wrong even when constructing the DateTime object from an ISO-8601 string.

This is not an issue with the system's timezone data, as both BASH date and PHP's own date() function work as expected (this is verified in 'test_ValidateData' below).

This is a fairly serious DateTime bug and I am shocked that it has not been addresses since 2012!


==========

<?php

/**
 * The native PHP DateTime appears to have a bug relating to the end of British Summer Time.
 *
 * The transition is supposed to take place at 01:59:59+01:00 to leap immediately
 * back to 01:00:00+00:00, however PHP's DateTime appears to actually revert to GMT at 01:00:00
 *
 * This relates to BOTH set/getTimestmap(), AND to setting the DateTime's value using setDate() & setTime().
 * Displaying the full ISO-8601 date-time in either of these cases shows the wrong time zone offset.
 *
 * setTimestamp() appears to assume that the timestamp you have given it is relative to the local timezone
 * during the end of Daylight Savings Time, and reduced the time by 1 hour.
 *
 * Calling getTimestamp() on DateTime objects at this edge case results in an offset timestamp, not the
 * expected epoch.
 *
 * Reported to PHP devs in 2012 (!)
 *
 * @see https://bugs.php.net/bug.php?id=63442
 *
 */
class DateTime_DST_Test extends PHPUnit_Framework_TestCase
{
	public function setUp()
	{
		date_default_timezone_set('Europe/London');
	}

	/**
	 * Check PHP's legacy date() function to validate the test data.
	 * @dataProvider DataProvider_DST_End
	 * @param $iEpoch
	 * @param $sISO8601
	 */
	public function test_ValidateData($iEpoch, $sISO8601)
	{
		$this->assertEquals($sISO8601, date('c', $iEpoch), 'date()');
	}

	/**
	 * Bug is present when setTimestamp() is used.
	 * @dataProvider DataProvider_DST_End
	 * @param	integer	$iEpoch
	 * @param	string	$sISO8601
	 */
	public function test_WrongTimezone_setTimestamp($iEpoch, $sISO8601)
	{
		$dtPHP = new DateTime();
		$dtPHP->setTimestamp($iEpoch);
		$this->assertEquals($iEpoch, $dtPHP->getTimestamp());
		$this->assertEquals($sISO8601, $dtPHP->format('c'));
	}

	/**
	 * Bug is present even when constructed without using setTimestamp().
	 * @dataProvider DataProvider_DST_End
	 * @param	integer	$iEpoch
	 * @param	string	$sISO8601
	 */
	public function test_WrongTimezone_SetFromISO($iEpoch, $sISO8601)
	{
		$dtPHP = new DateTime($sISO8601, new DateTimeZone('UTC'));
		$this->assertEquals($iEpoch, $dtPHP->getTimestamp());

		$dtPHP->setTimezone(new DateTimeZone('Europe/London'));
		$this->assertEquals($iEpoch, $dtPHP->getTimestamp());
		$this->assertEquals($sISO8601, $dtPHP->format('c'));
	}

	/**
	 * Bug is present even when the object is converted to UTC.
	 * @dataProvider DataProvider_DST_End
	 * @param	integer	$iEpoch
	 */
	public function test_WrongTimezone_SetToUTC($iEpoch)
	{
		$dtPHP = new DateTime();
		$dtPHP->setTimestamp($iEpoch);
		$dtPHP->setTimezone(new DateTimeZone('UTC'));
		$this->assertEquals($iEpoch, $dtPHP->getTimestamp());
	}

	/**
	 * Bug is NOT present if DateTime is in UTC when setTimestamp() is called.
	 * @dataProvider DataProvider_DST_End
	 * @param	integer	$iEpoch
	 */
	public function test_CorrectTimezone_StartInUTC($iEpoch)
	{
		date_default_timezone_set('UTC');
		$dtPHP = new DateTime();
		$dtPHP->setTimezone(new DateTimeZone('UTC'));
		$dtPHP->setTimestamp($iEpoch);
		$this->assertEquals($iEpoch, $dtPHP->getTimestamp());
	}

	/**
	 * Bug is present if getTimestamp() is called after converting back out out of UTC
	 * @dataProvider DataProvider_DST_End
	 * @param	integer	$iEpoch
	 * @param	string	$sISO8601
	 */
	public function test_WrongTimezone_StartInUTC_ChangeTimezone($iEpoch, $sISO8601)
	{
		date_default_timezone_set('UTC');
		$dtPHP = new DateTime();
		$dtPHP->setTimezone(new DateTimeZone('UTC'));
		$dtPHP->setTimestamp($iEpoch);
		$dtPHP->setTimezone(new DateTimeZone('Europe/London'));
		$this->assertEquals($iEpoch, $dtPHP->getTimestamp());
		$this->assertEquals($sISO8601, $dtPHP->format('c'));
	}


	/**
	 * Epochs right at the end of Daylight savings in Europe/London.
	 * ISO-8601 date-times are in the Europe/London timezone.
	 *
	 * @see test_DateTime_DST_End
	 * @return array [$iEpoch, $sISO8601]
	 */
	public static function DataProvider_DST_End()
	{
		return array(
			array(1414281599, '2014-10-26T00:59:59+01:00'), // Passes
			array(1414281600, '2014-10-26T01:00:00+01:00'), // Fails
			array(1414285199, '2014-10-26T01:59:59+01:00'), // Fails
			array(1414285200, '2014-10-26T01:00:00+00:00'), // Passes

			array(1445731199, '2015-10-25T00:59:59+01:00'), // Passes
			array(1445731200, '2015-10-25T01:00:00+01:00'), // Fails
			array(1445734799, '2015-10-25T01:59:59+01:00'), // Fails
			array(1445734800, '2015-10-25T01:00:00+00:00'), // Passes
		);
	}

}
 [2015-02-17 14:24 UTC] howard at zedcore dot com
System: Debian Wheezy
PHP 5.4.36-0+deb7u3
 [2015-04-20 03:10 UTC] pasindu@php.net
-Status: Open +Status: Verified
 [2020-11-05 12:56 UTC] cmb@php.net
-Status: Verified +Status: Not a bug -Assigned To: +Assigned To: derick
 [2020-11-05 12:56 UTC] cmb@php.net
Let's analyze the OP's test script.  When the first
::setTimestamp() is called, that results in 2012-10-28T02:00:00;
when the second ::setTimestamp() is called, that results in
2012-10-28T02:00:00.  And this is correct; the first timestamp
correlates to 2012-10-28T02:00:00 CET, and the second timestamp
correlates to 2012-10-28T02:00:00 CEST.  However, since CET/CEST
is not explicitly enforced, both times are 2012-10-28T02:00:00
Europe/Prague, and as such show the same timestamp.

In my humble opinion, the actual bug is the concept of DST.

I'm closing this as not a bug.  Derick, please reopen if my
assessment is wrong.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Sat Dec 21 18:01:29 2024 UTC