PHP: Get timezone identifier offset from UTC - php

I know \DateTimeZone::listIdentifiers() returns all timezone identifiers, but how can I retrieve their actual offset from UTC?

There is method getoffset
<?php
// Create two timezone objects, one for Taipei (Taiwan) and one for
// Tokyo (Japan)
$dateTimeZoneTaipei = new DateTimeZone("Asia/Taipei");
$dateTimeZoneJapan = new DateTimeZone("Asia/Tokyo");
// Create two DateTime objects that will contain the same Unix timestamp, but
// have different timezones attached to them.
$dateTimeTaipei = new DateTime("now", $dateTimeZoneTaipei);
$dateTimeJapan = new DateTime("now", $dateTimeZoneJapan);
// Calculate the GMT offset for the date/time contained in the $dateTimeTaipei
// object, but using the timezone rules as defined for Tokyo
// ($dateTimeZoneJapan).
$timeOffset = $dateTimeZoneJapan->getOffset($dateTimeTaipei);
// Should show int(32400) (for dates after Sat Sep 8 01:00:00 1951 JST).
var_dump($timeOffset);
?>

You can build an associative array mapping timezones and their offset using the getOffset method of DateTime instance.
getOffset returns the difference in seconds of a date evaluated in the time zone of the provided datetime argument (in our example, this is the current time in utc), and the same datetime as evaluated in the local time zone.
$utcNow = new DateTime('now', (new DateTimeZone('UTC')));
$identOffset = array_map(
function ($ident) use ($utcNow) {
$localTimeZone = new DateTimeZone($ident);
$offset = $localTimeZone->getOffset($utcNow);
return [$ident => $offset] ;
},
DateTimeZone::listIdentifiers()
);
$identOffset = array_merge(...$identOffset);
var_dump($identOffset);

If you are using https://carbon.nesbot.com/
// You will get timezone offset as string
Carbon::now('Asia/Kathmandu')->getOffsetString(); // +05:45

Related

DateTimeZone only handles offsets in one direction

I am attempting to retrieve a date that is not in GMT and convert it to GMT. To do this, I am creating two time zones (one GMT and one non-GMT) and attempting to get the offset between them. However, the offset is only correct in one direction. For this specific example, I am trying to compare GMT +4 to GMT. I expect to get 4hrs (14400 seconds) when I compare the the GMT timezone to the GMT+4 timezone, and -4hrs (-14400 seconds) when I compare the GMT+4 timezone to GMT. However, when comparing the later I'm getting 0... Here is what I have
$default_timezone = new DateTimeZone(drupal_get_user_timezone());
$default_reg_date = new DateTime($reg_date_string, $default_timezone);
$gmt_timezone = new DateTimeZone('UTC');
$gmt_reg_date = new DateTime($reg_date_string, $gmt_timezone);
// Returns as 14400
$default_gmt_offset = $default_timezone->getOffset($gmt_reg_date);
// Returns as 0
$gmt_default_offset = $gmt_timezone->getOffset($default_reg_date);
Why can't I get the right number, what am I doing wrong? Does retrieving the offset only work one way?
Note: in this specific example, drupal_get_user_timezone() is returning GMT+4
From the PHP documentation :
This function returns the offset to GMT for the date/time specified in the datetime parameter. The GMT offset is calculated with the timezone information contained in the DateTimeZone object being used.
The function works with 2 logics steps :
Convert the date into the timezone on which the function is applied
Get the offset from GMT
So calling this function on new DateTimeZone('UTC') will always returns 0
If you want to convert a date into UTC, you can use the setTimeZone function :
$date_string = '2020-05-01 09:11:00' ;
$date = new DateTime($date_string, new DateTimeZone('Europe/Brussels'));
echo $date->format('c') ; // 2020-05-01T09:11:00+02:00
$date->setTimeZone(new DateTimeZone('UTC'));
echo $date->format('c') ; // 2020-05-01T07:11:00+00:00

How does method timezone getOffset uses date on PHP?

I'm trying to understand how PHP uses the given datetime to return getOffset result on a DateTimeZone object. As far as I see, the result is always the same no matter what datetime I pass.
public function testGetOffset()
{
// UTC offset is 00:00 (0 seconds)
$timeZoneUtc = new \DateTimeZone('UTC');
$dateUTC = new \DateTime('now', $timeZoneUtc);
// America/Sao_Paulo offset is -02:00 (-7200 seconds)
$timeZoneSP = new \DateTimeZone('America/Sao_Paulo');
$dateSP = new \DateTime('now', $timeZoneSP);
// America/New_York offset is -05:00 (-18000 seconds)
$timeZoneNY = new \DateTimeZone('America/New_York');
$dateNY = new \DateTime('now', $timeZoneNY);
$this->assertEquals(0, $timeZoneUtc->getOffset($dateUTC)); // true
$this->assertEquals(0, $timeZoneUtc->getOffset($dateNY)); // true
$this->assertEquals(0, $timeZoneUtc->getOffset($dateSP)); // true
$this->assertEquals(-7200, $timeZoneSP->getOffset($dateUTC)); // true
$this->assertEquals(-7200, $timeZoneSP->getOffset($dateNY)); // true
$this->assertEquals(-7200, $timeZoneSP->getOffset($dateSP)); // true
$this->assertEquals(-18000, $timeZoneNY->getOffset($dateUTC)); // true
$this->assertEquals(-18000, $timeZoneNY->getOffset($dateNY)); // true
$this->assertEquals(-18000, $timeZoneNY->getOffset($dateSP)); // true
}
Can anyone help me get that?
The offset changes only during a DST transition. New York will transition from winter time to summer time on March 11. getOffset tells you what the offset for that timezone is at that particular time. All the times you're giving it are all today, well before the DST transition date, so they all result in the same offset. Try getting the offset for a date before and after March 11 to see a difference.
A date is an absolute moment in time, it doesn't depend on a timezone. The timezone is used only when the date is formatted as string.
Internally, all three DateTime objects created by the code store the same value: the number of seconds (and microseconds on PHP 7) since 1970-01-01 00:00:00 UTC.

Creating timestamp for 5/13/2014 # 3pm

I have an application that needs to send a UTC timestamp in order for it to work correctly. In my application a user can have any number of timezones. So if they pick 3pm and their timezone is America/New_York, it is a different 3pm than if it was America/Chicago.
I need to figure out a way to change the date into the right UTC timestamp. I know I can use date_default_timezone_set("UTC")...but I don't think will work correctly.
I think I need to calculate a difference between UTC and regular timezone, but I am not sure. Any advice is welcomes.
date_default_timezone_set("UTC");
echo strtotime('5/13/2014 3:00 PM');
1399993200
date_default_timezone_set("America/New_York");
echo strtotime('5/13/2014 3:00 PM');
1400007600
As you can tell these 2 values are different.
EDIT: Here is what my code looks like. It doesn't seem to work correctly as the application doesn't show the event in the right time.
$previous_timezone = date_default_timezone_get();
date_default_timezone_set("UTC");
$aceroute_schedule = $this->sale_lib->get_send_to_aceroute_schedule();
if (($start_time = strtotime($aceroute_schedule['aceroute_schedule_date']. ' '.$aceroute_schedule['aceroute_schedule_time_start'])) !== FALSE)
{
//Append 000 as as string for 32 bit systems
$start_epoch = $start_time.'000';
$end_epoch = strtotime('+ '.$aceroute_schedule['aceroute_duration'].' minutes', $start_time).'000';
}
else //Default to current time + 1 hour
{
//Append 000 as as string for 32 bit systems
$start_epoch = time().'000';
$end_epoch = strtotime('+1 hour', time()).'000';
}
$event->start_epoch = $start_epoch;
$event->end_epoch = $end_epoch;
Update:
This will now create a DateTime object in the user's DateTimeZone ('America/New_York'). And then it will set that object's timezone to UTC. To get the timestamp (or other string representations of date), use ::format().
# Create NY date
$NY = new DateTimeZone("America/New_York");
$NYdate = new DateTime('5/13/2014 3:00 PM', $NY);
# Set timezone to UTC
$UTC = new DateTimeZone("UTC");
$UTCdate = $NYdate->setTimezone($UTC);
# Get timestamp (PHP 5.2 compatible)
$timezone = $UTCdate->format('U');
var_dump($timezone); // a string containing UNIX timestamp
First I create 2 DateTime objects based off of their respective DateTimeZone objects. Then we can either use OOP ::diff() to get another object containing information about the time difference. Or we can use simple integers representing the difference in seconds from ::getTimestamp.
$date = '5/13/2014 3:00 PM';
# Create NY date
$NY = new DateTimeZone("America/New_York");
$NYdate = new DateTime($date, $NY);
# Create UTC date
$UTC = new DateTimeZone("UTC");
$UTCdate = new DateTime($date, $UTC);
# Find difference object
$diff = $NYdate->diff($UTCdate);
var_dump($diff); // a DateInterval object containing time difference info
# Find difference in seconds
$diff = $NYdate->getTimestamp() - $UTCdate->getTimestamp();
var_dump($diff); // an integer containing time difference in seconds
Links:
DateTimeZone
DateTime
DateInterval
Example in http://www.php.net/manual/en/datetime.settimezone.php
$date = new DateTime('2000-01-01', new DateTimeZone('Pacific/Nauru'));
echo $date->format('Y-m-d H:i:sP') . "\n";
$date->setTimezone(new DateTimeZone('Pacific/Chatham'));
echo $date->format('Y-m-d H:i:sP') . "\n";
The first line creates a DateTIme object, using the timezone Pacific/Nauru.
You can then change the timezone using setTimezone as shown in line 4, and the output will be modified accordingly.
note: the default timezone (if you don't specify it in the 2nd parameter in line 1) is the one set in your php.ini file, which you can modify (at runtime) with date_default_timezone_set("America/New_York")
note2: the 1st parameter in line 1, is equivalent to the 1st parameter of the strtotime function.
note3: the format method takes the same format parameter as date (http://www.php.net/manual/en/function.date.php)

Converting GMT time to local time using timezone offset, not timezone identifier

It's pretty easy to convert a given GMT date into local time if you're given the timezone identifier from this list in PHP: http://www.php.net/manual/en/timezones.php
For example, you can do this (where $fromTimeZone is just 'GMT', $toTimeZone is just one of the constants from that list (i.e. 'America/Chicago'), and $datetime is the GMT date):
public static function convertToTimezone($datetime, $fromTimeZone, $toTimeZone, $format = 'Y-m-d H:i')
{
// Construct a new DateTime object from the given time, set in the original timezone
$convertedDateTime = new DateTime($datetime, timezone_open($fromTimeZone));
// Convert the published date to the new timezone
$convertedDateTime->setTimezone(timezone_open($toTimeZone));
// Return the udpated date in the format given
return $convertedDateTime->format($format);
}
However, I'm having issue converting the same GMT date to the local time if just given the timezone offset. For instance, instead of being given 'America/Chicago', I'm given -0500 (which is the equivalent offset for that timezone).
I've tried things such as the following (where $datetime is my GMT date and $toTimeZone is the offset (-0500 in this case)):
date($format, strtotime($datetime . ' ' . $toTimeZone))
I know all the date() sort of functions are based on the servers's timezone. I just can't seem to get it to ignore that and use a timezone offset that is given explicitly.
You can convert a specific offset to a DateTimeZone:
$offset = '-0500';
$isDST = 1; // Daylight Saving 1 - on, 0 - off
$timezoneName = timezone_name_from_abbr('', intval($offset, 10) * 36, $isDST);
$timezone = new DateTimeZone($timezoneName);
Then you can use it in a DateTime constructor, e.g.
$datetime = new DateTime('2012-04-21 01:13:30', $timezone);
or with the setter:
$datetime->setTimezone($timezone);
In the latter case, if $datetime was constructed with a different timezone, the date/time will be converted to specified timezone.

PHP: Why is this code erroneously returning zero?

I'm calculating the offset between two timezones, but I'm seeing a result I don't expect (zero) with the following code:
$datetimezone_london = new DateTimeZone('Europe/London');
$datetimezone_client = new DateTimeZone('Australia/Canberra');
$now_client = new DateTime("now", $datetimezone_client);
$offset = $datetimezone_london->getOffset($now_client);
echo $offset;
If I flip the timezone strings, it works, but surely the above code should work too. What's happening?
getOffset() returns the offset to GMT in seconds, and London is currently on GMT, hence the return value is zero.
I believe what you need instead is:
$tz_london = new DateTimeZone('Europe/London');
$tz_client = new DateTimeZone('Australia/Canberra');
$time_london = new DateTime('now', $tz_london);
$time_client = new DateTime('now', $tz_client);
$offset = $time_client->getOffset() - $time_london->getOffset();
echo $offset;
This currently (in January) returns 39600 (11 hours). In July it returns 9 hours, and in mid October (where there's a short period when both Europe and Australia are in daylight saving) it returns 10 hours.
DateTimeZone::getOffset() works a bit differently that what most people think. It calculates the offset to GMT of the instance DateTimeZone offset for the date passed as parameter. The date passed as parameter is then converted to the same timezone as the instance (DST and other rules applying) and the offset is calculated for that date.
So your code right now calculates the offset to GMT of the timezone Europe/London.. Since Europe/London is on GMT right now (versus BMT), you are getting 0. (Try a date in August, you'll get 36000).
If you want the current difference between two timezones, use this code...
function timezone_diff($origin, $compareTo, $forDate = "now") {
$dtzOrigin = new DateTimeZone($origin);
$dtzCompareTo = new DateTimeZone($compareTo);
$compareDate = new DateTime($forDate);
$offsetOrigin = $dtzOrigin->getOffset($compareDate);
$offsetCompareTo = $dtzCompareTo->getOffset($compareDate);
return $offsetCompareTo - $offsetOrigin;
}

Categories