PHP timezone. Why offset is zero? - php

I can't figure out how timezones work. Why this code produces zero as ouput? Shouldn't it return 10800 instead of 0?
$myZone = new DateTimeZone('+0300');
$utcZone = new DateTimeZone('+0000');
$dateA = new DateTime('now', $myZone);
var_dump($utcZone->getOffset($dateA)); // 0
$dateA->setTimezone($utcZone);
var_dump($utcZone->getOffset($dateA)); // 0
$dateA->modify('+ 3600 seconds');
var_dump($utcZone->getOffset($dateA)); // 0
It seems like $utcZone->getOffset(...) does not care about it's argument timezone and time diff at all. Where is the logic?

<?php
$myZone = new DateTimeZone('+0300');
$utcZone = new DateTimeZone('+0000');
$dateA = new DateTime('now', $myZone);
$timeOffset = $myZone->getOffset($dateA);
var_dump($timeOffset);
This one will return 10800 as you said. It was the other way around in getOffset.
output is : int(10800)

getOffset returns the offset used by the timezone at a specific moment.
Timezones can have more than one offset during history - the most common case is Daylight Saving Time, when the offset changes for a specified period. So depending on the time of the year, a certain timezone can have a different offset.
The datetime parameter defines the instant you want to check, and getOffset will check in the timezone's history and return the one used at that specific datetime - no matter what the datetime's timezone is. In the documentation you can find an example of that: http://php.net/manual/en/datetimezone.getoffset.php#refsect1-datetimezone.getoffset-examples
That said, when you use fixed offset values such as "+0000", this means that the DateTimeZone object will always have that offset, all the time - it never changes. That's why when you call $utcZone->getOffset, it always returns zero, no matter what datetime parameter you pass.
If you want the date's offset, call the getOffset method on the DateTime object.

Related

PHP: How can one find the time difference in seconds between values "2022-06-24T15:04:37.000000Z" and "2022-06-24 15:05:15", e.g. using strtotime()?

I am using an API that is giving me two time values to describe its execution, but in different formats. For instance:
'created_at' => '2022-06-24T15:04:37.000000Z',
'processed_at' => '2022-06-24 15:05:15',
I am running the code:
$created = "2022-06-24T15:04:37.000000Z";
$processed = "2022-06-24 15:05:15";
$createdtime = strtotime($created);
$processedtime = strtotime($processed);
$diff = $processedtime - $createdtime;
echo "Created: $created Processed: $processed createdtime: $createdtime processedtime: $processedtime Diff: $diff<br/>\n";
On two different servers, to test how to find the difference in seconds between these two times. One on server it gives me the results:
Created: 2022-06-24T15:04:37.000000Z Processed: 2022-06-24 15:05:15 createdtime: 1656083077 processedtime: 1656083115 Diff: 38
On another server, it says:
Created: 2022-06-24T15:04:37.000000Z Processed: 2022-06-24 15:05:15 createdtime: 1656083077 processedtime: 1656097515 Diff: 14438
I am assuming that this is a timezone issue. 14400 seconds=10 hours. The "real" answer should be 38, as that's how long the API took in real life to process the request. Is there any code I could use to get a consistent result of 38 seconds across the two servers?
processed_at doesn't contain a timezone, so PHP is using whatever it has configured as default. It looks like the timezone of processed_at is supposed to be UTC, so I'd just force it. You could just add a Z to the end of the string:
$processedtime = strtotime($processed . ' Z');
Or, more correctly, tell PHP to use UTC when no timezone is otherwise available in the string:
$processedtime = (new DateTime($processed, new DateTimeZone('UTC')))->format('U');
[Update]
Is there a way to check if a string has a blank TZ?
Yes, but it's easier to just use the default mechanism of the DateTime class. For example, if you do this:
$timestamp1 = new DateTime($string1, new DateTimeZone('UTC'));
Then this default timezone will only be applied if string doesn't explicitly contain one. But if you do this:
$timestamp1 = new DateTime($string1);
$timestamp1->setTimezone(new DateTimeZone('UTC'));
Then you will always explicitly set the timezone, possibly changing it from the original specified. If you do both:
$timestamp1 = new DateTime($string1, new DateTimeZone('UTC'));
$timestamp1->setTimezone(new DateTimeZone('UTC'));
Then you're saying, "consider this string to be UTC if it doesn't contain timezone, and then convert it to UTC if it did contain a timezone that wasn't UTC."
Is there maybe any way to check if DateTime generated a timezone for an input string?
You can pull out the timezone that was used in the previous step and apply it as the default for the second timestamp, if that second timestamp doesn't also include an explicit timezone:
$timestamp1 = new DateTime($string1, new DateTimeZone('UTC'));
$timestamp2 = new DateTime($string2, $timestamp1->getTimezone());

Can someone tell me how the DateTimeZone::getOffset PHP function works?

I got this line of code:
echo (new DateTimeZone('UTC'))->getOffset(new DateTime('now', new DateTimeZone('America/New_York')));
It returns zero. But I expected it to return the difference between UTC and America/New_York time zones.
I then provided the same time zone twice:
echo (new DateTimeZone('America/New_York'))->getOffset(new DateTime('now', new DateTimeZone('America/New_York')));
I expected it to return zero because the time zones are the same. But it now returns -18000 (That's the offset between America/New_York and UTC).
In the documentation it says that getOffset() always returns the offset to GMT (=UTC). But then why is getOffset not static? And if it is always the offset relative to GMT why does the time zone in the first constructor play a role?
I know there's another getOffset method in the DateTime class which is easier to use. But i want to understand how the getOffset method in the DateTimeZone class works.
The offset of a timezone is how many hours it differs from UTC. The offset of a timezone may change throughout the year depending on when DST goes into and out of effect. So you cannot get the offset of a timezone just like that, since it's not a constant value. You can only interrogate a timezone what its offset from UTC is at a given time. That's what you pass the DateTime object to getOffset for: to ask the timezone what its offset value is for the given time. E.g.:
echo (new DateTimeZone('America/New_York'))->getOffset(new DateTime), PHP_EOL;
echo (new DateTimeZone('America/New_York'))->getOffset(new DateTime('+6 months'));
-18000
-14400
If you want to get the offset difference between two arbitrary timezones, you get their respective UTC offsets and subtract them.
I came here looking for a way to get the offset between PHP's current default timezone and UTC. I went down the rabbit hole with DateTimeZone::getOffset, but it turns out there's a much easier way:
$offset = date('O');
// Returns a string like '-0400'
$offset = date('P');
// Returns a string like '-04:00'
$offset = date('Z');
// Returns the offset in seconds, e.g. '-14400'
To be clear, this gets whatever the timezone offset is right now, but I imagine that's what is wanted most of the time.
You can switch PHP's current timezone with date_default_timezone_set().
See https://www.php.net/manual/en/datetime.format.php

How to set php timezone by offset value in hh:mm, minuts or seconds?

My online application gets users timezone offset in + or - value in minutes. I want to set this value in server.
MySQL / MariaDB accepts timezone values and works perfectly
SET time_zone = "+0:15";
But php only accepts timezone string like this
date_default_timezone_set("Asia/Karachi"); // Works Perfectly
And does not set time in offset value
date_default_timezone_set("+0:15"); // Error
Is there any way to set php time with offset value?
After reading the documentation for the date_default_timezone_set() php function, this seems to be a common issue.
I'm sure there may be alternatives if you choose to use other date functions in php, however, to be able to set the timezone by an offset, you would require a helper function to do this.
I've copied and modified a helper function posted on the php doc page here
function setTimezoneByOffset($offset)
{
$testTimestamp = time();
date_default_timezone_set('UTC');
$testLocaltime = localtime($testTimestamp,true);
$testHour = $testLocaltime['tm_hour'];
$abbrarray = timezone_abbreviations_list();
foreach ($abbrarray as $abbr)
{
//$abbr."<br>";
foreach ($abbr as $city)
{
date_default_timezone_set($city['timezone_id']);
$testLocaltime = localtime($testTimestamp,true);
$hour = $testLocaltime['tm_hour'];
$testOffset = $hour - $testHour;
if($testOffset == $offset)
{
return $city['timezone_id'];
}
}
}
return false;
}
Where the parameter $offset is a string representation of the offset (ie. '+0:15'), which returns the timezone id of the timezone which matches that of your input.
My online application gets users timezone offset in + or - value in minutes.
This is problematic, because a time zone offset is not the same thing as a time zone. Consider that one cannot take the current offset and assume it is the correct offset to apply for any given date and time. Many time zones use more than one offset depending on time of year, and many time zones have had changes to their standard time offsets at different points in history. For more on this, see "Time Zone != Offset" in the timezone tag wiki.
As an example, you might be using a JavaScript function like new Date().getTimezoneOffset(). However, this returns the offset for the Date object returned by new Date() - which is the current date. For me, in the US Pacific time zone, it will return 480 (UTC-8) when run in the winter during standard time, but 420 (UTC-7) when run in the summer during daylight saving time.
Instead, have your front-end call Intl.DateTimeFormat().resolvedOptions().timeZone in JavaScript to return the time zone identifier, which for me returns "America/Los_Angeles". You can pass that to your back-end and use it with PHP.
As a side note, there are no time zones in the world that use or have ever used UTC+0:15. The closest to that would have been UTC+0:20 used in the Netherlands from 1 May 1909 to 16 May 1940. This is already captured in the history of "Europe/Amsterdam".

get timezone offset from UNIX timestamp in PHP

In PHP 7.2, if I do this:
<?php
$dt = new DateTime('#1522680410', new DateTimeZone(date_default_timezone_get()));
$tz_offset = $exp->getOffset();
?>
$tz_offset always returns 0. However, if I set a date and not a UNIX timestamp (i.e., '2018-01-02' instead of '#1522680410') it shows the correct offset value.
Is there a way to have the timestamp return the timezone offset in one step like above?
Unix timestamp is always in UTC timezone (or ±00:00 offset). If not, you are doing something nasty :)
If you take a look at DateTime::__construct(), you will see note on 2nd argument:
The $timezone parameter and the current timezone are ignored when the $time parameter either is a UNIX timestamp (e.g. #946684800) or specifies a timezone (e.g. 2010-01-28T15:00:00+02:00).
Change timezone after you have created DateTime object:
$dt = new DateTime('#1522680410');
$dt->setTimezone(new DateTimeZone(date_default_timezone_get()));
Apparently you can do it this way:
<?php
$dt = new DateTime(DateTime::createFromFormat('U', '1522680410')->format('Y-m-d'), new DateTimeZone(date_default_timezone_get()));
?>

Calculate if DST active for a given timestamp and offset

Given a unix timestamp and timezone offset relative to UTC (e.g. -5), is it possible to calculate if DST is active?
I can do something like:
<?php
echo 'DST enabled? ' . date('I', 1345731453) ."<br>";
?>
But I cannot see how to pass in an offset or a TimeZone.
Im guessing also that there is a difference between the timezone and the offset, given that specific countries support DST?
Appreciate any thoughts.
Correct, you need to start with date and tz to determine offset.
in 5.3+ you can get transitions for start/end period, so you can just use your timestamp for both, something like this:
$inputTime = $your_unix_timestamp;
$inputTZ = new DateTimeZone('Europe/London');
$transitions = $inputTZ->getTransitions($inputTime,$inputTime);
$offset = $transitions[0]['offset'];
$offset is the DST offset at the time in question
Get the timezone with:
$old_zone = ini_get('date.timezone');
Change it to the one you want:
ini_set('date.timezone', 'Europe/Brussels');
Revert back:
ini_set('date.timezone', $old_zone);
The PHP timestamp is always UTC. Depending on your timezone setting it determine what time to show.

Categories