PHP Date after time change in Europe - php

This morning I ran unit tests and they failed.
Tests took offers from last 48 hours by calculating time with:
date('U', time() - 48 * 3600)
When I used:
(new DateTime('-48 hours'))->format('U')
it shown one hour difference.
May the reason be that on sunday in Poland time was moved one hour backwards

I presume your server runs with Europe/Warsaw as default time zone. PHP date/time calculations are often incorrect if they cross DST boundaries, as it's the case here. I suggest you do all maths in UTC and convert from/to local time as needed.
Please compare:
$warsaw = new DateTimeZone('Europe/Warsaw');
$utc = new DateTimeZone('UTC');
$start = new DateTime('2018-10-28 04:30:00', $warsaw);
$start->modify('-4 hours');
var_dump($start);
$start = new DateTime('2018-10-28 04:30:00', $warsaw);
$start->setTimezone($utc);
$start->modify('-4 hours');
$start->setTimezone($warsaw);
var_dump($start);
object(DateTime)#3 (3) {
["date"]=>
string(26) "2018-10-28 00:30:00.000000"
["timezone_type"]=>
int(3)
["timezone"]=>
string(13) "Europe/Warsaw"
}
object(DateTime)#4 (3) {
["date"]=>
string(26) "2018-10-28 01:30:00.000000"
["timezone_type"]=>
int(3)
["timezone"]=>
string(13) "Europe/Warsaw"
}
The Unix timestamp version of your code should be unaffected because a Unix timestamp is a fixed moment in time (thus doesn't switch with time zones).

Related

Incorrect time showing after converting to date format - PHP

Below is the time string I have,
'2019-12-30T15:42:33.891+11:00'
I tried to convert this to date format using below php code.
date('Y-m-d H:i:s',strtotime('2019-12-30T15:42:33.891+11:00'));
And i am getting 2019-12-30 10:12:33 as the output. It seems date getting correctly but time not.
How to display the time also correctly? Thanks in advance!
This date and time format relates to the "Zulu time" (UTC). UTC is refereed as Universal Time Coordinated. It is also known as “Z time” or “Zulu Time”.
You should change your timezone by some command like below code:
$date = new DateTime('2019-12-30T15:42:33.891+11:00', new DateTimeZone('UTC'));
echo $date->format('Y-m-d H:i:s');
Or, for having more tools you can use the Carbon library.
Your time string '2019-12-30T15:42:33.891+11:00' contains time zone information of +11:00.
With date() the date and time are converted into the local time zone of your server. For example, I get "2019-12-30 05:42:33" (Timezone Europe/Berlin).
You can set a different time zone with date_default_timezone_set() or use DateTime to output the time for a location with the time zone "+11:00".
echo date_create('2019-12-30T15:42:33.891+11:00')->format('Y-m-d H:i:s');
//2019-12-30 15:42:33
strtotime () returns an integer timestamp. The Iformation of the Timezone +11:00 are lost in the process, but are processed when the time stamp is determined.
The DateTime object always has a time zone.
echo '<pre>';
var_dump(date_create('2019-12-30T15:42:33.891+11:00'));
Output:
object(DateTime)#1 (3) {
["date"]=>
string(26) "2019-12-30 15:42:33.891000"
["timezone_type"]=>
int(1)
["timezone"]=>
string(6) "+11:00"
}
Note (Update):
When creating a DateTime object, the optional parameter for the time zone is only taken into account if the time string does not contain a time zone!
var_dump(new DateTime('2019-12-30T15:42:33.891 +11:00', new DateTimeZone('UTC')));
object(DateTime)#1 (3) {
["date"]=>
string(26) "2019-12-30 15:42:33.891000"
["timezone_type"]=>
int(1)
["timezone"]=>
string(6) "+11:00"
}
//without +11:00
var_dump(new DateTime('2019-12-30T15:42:33.891', new DateTimeZone('UTC')));
object(DateTime)#1 (3) {
["date"]=>
string(26) "2019-12-30 15:42:33.891000"
["timezone_type"]=>
int(3)
["timezone"]=>
string(3) "UTC"
}

The function setTimezone changes the timezone but not the date

I am trying to change the TimeZone of my date from Europe/Berlin to UTC before storing in it into the DB.
When I use the setTimezone() method of the DateTime object, it changes the timezone property but not the date itself.
Example code :
$dt = new \DateTime();
var_dump($dt);
$dt->setTimezone(new \DateTimeZone('Indian/Comoro'));
var_dump($dt);
$dt->setTimezone(new \DateTimeZone('UTC'));
var_dump($dt);
And the result :
object(DateTime)#1479 (3) { ["date"]=> string(26) "2019-02-18 14:12:37.521579" ["timezone_type"]=> int(3) ["timezone"]=> string(13) "Europe/Berlin" }
object(DateTime)#1479 (3) { ["date"]=> string(26) "2019-02-18 14:12:37.521579" ["timezone_type"]=> int(3) ["timezone"]=> string(13) "Indian/Comoro" }
object(DateTime)#1479 (3) { ["date"]=> string(26) "2019-02-18 14:12:37.521579" ["timezone_type"]=> int(3) ["timezone"]=> string(3) "UTC" }
Berlin is UTC+1 and Comoro is UTC+3, but the date doesn't change.
I'm working with Symfony 4 on Vagrant.
This test is made inside of a controller
Is it possible that my Timezones are all set to +00 ?
What kind of tests could I implement to find the cause of this problem ?
UPDATE
This returns me an offset of 0 in my controller.
It returns 3600 with php CLI.
$tz = new \DateTimeZone('Europe/Berlin');
var_dump($tz->getOffset(new \DateTime('now', new \DateTimeZone('UTC'))));
The problem was from the tzdata package which worked well after updating.
I had to restart apache to make it work in my app.
The timestamp value represented by the DateTime object is not modified when you set the timezone using this method. Only the timezone, and thus the resulting display formatting, is affected.
Try this (assuming the date is set to Europe/Berlin timezone by default):
$dt = new \DateTime();
Pass through the timezone UTC:
$timezone = new \DateTimeZone('UTC');
we then need to get the offset.
$offset = $timezone->getOffset($dt);
lastly, modify the date using the offset:
$dt->modify($offset . 'seconds');

PHP DateTime with TimeZone to MongoDB\BSON\UTCDateTime

I need help with PHP DateTime with TimeZone converting to MongoDB\BSON\UTCDateTime.
If I have string "2015-10-20T04:02:00.608000+01:00" it gives me a DateTime
$date = DateTime::createFromFormat( 'Y-m-d\TH:i:s.uT', $string );
DateTime
date => "2015-10-20 04:02:00.608000"
timezone_type => 1
timezone => "+01:00"
If I convert it to MongoDB\BSON\UTCDateTime and convert back to PHP DateTime
$mDate = new \MongoDB\BSON\UTCDateTime( $date->format('U') * 1000 );
$mDate->toDateTime()->setTimeZone(new DateTimeZone('Europe/Bratislava'))
I will get a correct result 05:02
DateTime
date => "2015-10-20 05:02:00.000000"
timezone_type => 3
timezone => "Europe/Bratislava"
BUT if the input string has +02:00 TimeZone "2015-10-20T04:02:00.608000+02:00" and use the same approach the result is
DateTime
date => "2015-10-20 04:02:00.000000"
timezone_type => 3
timezone => "Europe/Bratislava"
Why is the second result 04:02 if I expect 06:02?
The response is correct. When you are adding a + timezone onto the timestamp, you are, in effect going East from UTC. You are not adding to the UTC time, you are actually subtracting from what the time is in UTC. This means that 2015-10-20T04:02:00.608000+01:00 is 3am UTC. 2015-10-20T04:02:00.608000+02:00 is 2am UTC. You can see this more easily if you keep going higher with your timezone offsets
$date = DateTime::createFromFormat( 'Y-m-d\TH:i:s.uT', "2015-10-20T04:02:00.608000+01:00");
$mDate = new \MongoDB\BSON\UTCDateTime( $date->format('U') * 1000 );
var_dump($date, $mDate->toDateTime());
object(DateTime)#1 (3) {
["date"]=>
string(26) "2015-10-20 04:02:00.608000"
["timezone_type"]=>
int(1)
["timezone"]=>
string(6) "+01:00"
}
object(DateTime)#3 (3) {
["date"]=>
string(26) "2015-10-20 03:02:00.000000"
["timezone_type"]=>
int(1)
["timezone"]=>
string(6) "+00:00"
}
$date = DateTime::createFromFormat( 'Y-m-d\TH:i:s.uT', "2015-10-20T04:02:00.608000+02:00");
$mDate = new \MongoDB\BSON\UTCDateTime( $date->format('U') * 1000 );
var_dump($date, $mDate->toDateTime());
object(DateTime)#1 (3) {
["date"]=>
string(26) "2015-10-20 04:02:00.608000"
["timezone_type"]=>
int(1)
["timezone"]=>
string(6) "+02:00"
}
object(DateTime)#3 (3) {
["date"]=>
string(26) "2015-10-20 02:02:00.000000"
["timezone_type"]=>
int(1)
["timezone"]=>
string(6) "+00:00"
}
MongoDB stores the UTC timestamp. When you are adding the Europe/Bratislava timezone, you are saying, "What time is in Bratislava for this UTC timestamp". For October (daylight savings time), it's a 1 hr difference.
On a side note. Try to never mix +XXXX and Unicode/Olson timezones (Europe/Bratislava). You will wind up with some very strange errors due to daylight savings time. If you need to record a user's local time to display back at some point, Create your DateTime objects with the optional 3rd parameter like:
$customerTz = 'Europe/Bratislava';
$date = DateTime::createFromFormat( 'Y-m-d\TH:i:s.u', $dateString, $customerTz);
Also check to see if you really need to create a DateTime at all, or just a new UTCDateTime directly with a timestamp and deal with the tz's in the display logic.

Difference in dates between servers

I am wondering why I have been getting totally invalid computation by PHP all the time.
I have code like this:
echo floor((strtotime("2017-03-27") - strtotime("2017-03-24"))/86400);
which on the one of the server returns: 3 (like 3 days)
and on the another server returns: 2! (2.9583333333333 day?) Why is there a variance?
Or, just use better methods:
EDIT I updated the code to use the same dates as OP. It returns 3, as it should, and I'm fairly confident it will do that on whatever server you put it on to test.
<?php
$date1 = date_create('2017-03-27');
$date2 = date_create('2017-03-24');
$interval = date_diff($date1,$date2);
echo $interval->format('%a');
?>
Ref: http://php.net/manual/en/function.date-diff.php
Your servers maybe sitting in two different time zones. Convert the days to hours and you will get a difference of one hour.
3 days * 24 hours/1 day = 72 hours
2.958333 days * 24 hours/1 day = 71 hours
Since some people are having difficulty accepting that this is due to a TZ-specific DST change, have some proof:
function poc($tz_str) {
$tz = new DateTimeZone($tz_str);
$start = (new DateTime('2017-03-24', $tz))->format('U');
$end = (new DateTime('2017-03-27', $tz))->format('U');
return [$tz_str, $end - $start, ( $end - $start ) / 86400];
}
var_dump(
poc('UTC'),
poc('Europe/London'),
poc('America/New_York')
);
Output:
array(3) {
[0]=> string(3) "UTC"
[1]=> int(259200)
[2]=> int(3)
}
array(3) {
[0]=> string(13) "Europe/London"
[1]=> int(255600)
[2]=> float(2.9583333333333)
}
array(3) {
[0]=> string(16) "America/New_York"
[1]=> int(259200)
[2]=> int(3)
}
UTC has no DST, not affected.
Most of the eastern hemisphere has their DST change on March 26, affected.
Most of the western hemisphere has their DST change on March 12, not affected.
That said, don't do manual date math on a Unix timestamp.

compare datetime with different timezone_type php

I'm trying to compare to DateTime objects in PHP.
$Time1 = DateTime::createFromFormat('UP', '1409900072+0200');
$Time2 = new DateTime('2014-09-05 07:54:32');
The Time2 use the defoult which is Europe/Copenhagen, comparing yields the following
if ($Time2 > $Time1){
echo "true \n";
} else {
echo "false \n";
}
true
object(DateTime)#1 (3) {
["date"]=>
string(19) "2014-09-05 06:54:32"
["timezone_type"]=>
int(1)
["timezone"]=>
string(6) "+02:00"
}
object(DateTime)#2 (3) {
["date"]=>
string(19) "2014-09-05 07:54:32"
["timezone_type"]=>
int(3)
["timezone"]=>
string(17) "Europe/Copenhagen"
}
The way I understand it is the actual local time for Time1 is 08:54:32, so how can I get the comparison at the same timezone?
Thanks in advance
The really weird part is your initial value of 1409900072+0200. If 1409900072 is a UNIX timestamp, passing a particular timezone with it makes little sense. And it seems to cause PHP to create the instance incorrectly; it creates the instance with the time set to the UTC value (6:54), but the timezone offset of +0200 (where the time should actually be 8:54).
Arguably this should be filed as a bug report; but arguably the input data is nonsensical to begin with.
If you're feeding in a UNIX timestamp, then ignore any timezone information it may contain and explicitly set the timezone to UTC, then it all works as expected:
$t1 = DateTime::createFromFormat('U+', '1409900072+0200', new DateTimeZone('UTC'));
$t2 = new DateTime('2014-09-05 07:54:32', new DateTimeZone('Europe/Copenhagen'));
var_dump($t1 > $t2); // true
Note that PHP before 5.3.9 seems to have problems with the createFromFormat call; you'll probably have to filter out the trailing timezone by hand if you need to support those versions.
Convert both DateTimes to UTC (setTimeZone('UTC')) and then compare them.
<?php
$Time1 = DateTime::createFromFormat('UP', '1409900072+0200');
$Time2 = new DateTime('2014-09-05 07:54:32');
// convert
$utc = new DateTimeZone('UTC');
$time1_utc = clone $Time1;
$time1_utc->setTimeZone($utc);
$time2_utc = clone $Time2;
$time2_utc->setTimeZone($utc);
var_dump($Time1,$Time2);
var_dump($time1_utc,$time2_utc);

Categories