Get timezone location from offset - php

I need to set timezone in php. I have the offset in seconds of the new timezone and I want to set it using date_default_timezone_set. I have no probelm converting the seconds into offset like +0200 but I don't think it's enough.
From what I understood after reading the manual, is that I need to give as parameter something like America\New_York. Is there a way of converting the offset into a specific location?

You actually can't reliably do this. Each named time zone has multiple unique properties that change over time. The current offset is only part of the information. You also have to consider how daylight saving time applies, which could apply differently (or not at all) per time zone. You also have to consider how a single time zone can have a history of different values as they may have changed their offsets or DST rules many times over.
You should also consider that at any given time, several different time zones will be using the same offset. If you just pick one at random, you are ignoring a lot of important details!
Please read the timezone tag wiki.
The other answers here may return a value, but they are all based on false assumptions. A time zone simply can't be represented by a number alone.

You can search the output of DateTimeZone::listAbbreviations() for timezones which match your offset:
function convertTimezoneOffsetToId($offsetSeconds)
{
$ids = array();
foreach (DateTimeZone::listAbbreviations() as $abbrev) {
foreach ($abbrev as $zone) {
if ($zone['offset'] == $offsetSeconds) {
$ids[] = $zone['timezone_id'];
}
}
}
return array_unique($ids);
}
For example:
convertTimezoneOffsetToId(5040);
returns:
Array
(
[0] => Europe/Vilnius
[1] => Europe/Warsaw
)
Edit
As Matt points out, this function doesn't take into account the historical changes in DST.
So here's an improved function which does:
function convertTimezoneOffsetToId($offsetSeconds, $unixTimestamp)
{
$ids = array();
foreach (DateTimeZone::listIdentifiers() as $id) {
$dtz = new DateTimeZone($id);
$trans = $dtz->getTransitions($unixTimestamp, $unixTimestamp);
if ($trans[0]['offset'] == $offsetSeconds) {
$ids[] = $id;
}
}
return array_unique($ids);
}
It uses DateTimeZone::getTransitions() to get each timezone's offset at a particular moment in history.
For example:
convertTimezoneOffsetToId(19800, time());
returns a list of timezones which have an offset of 19800 seconds right now:
Array
(
[0] => Asia/Colombo
[1] => Asia/Kolkata
)
and:
convertTimezoneOffsetToId(19800, gmmktime(0, 0, 0, 1, 1, 2000));
returns a list of timezones which had an offset of 19800 seconds on 1st Jan 2000:
Array
(
[0] => Asia/Kolkata
)
Notice that Asia/Colombo has disappeared, because on 1st Jan 2000 its offset was 21600 seconds.

There is, yes. I'll just quote this excellent answer by uınbɐɥs:
$timezone = '+2:00';
$timezone = preg_replace('/[^0-9]/', '', $timezone) * 36;
$timezone_name = timezone_name_from_abbr(null, $timezone, true); //= Europe/Paris
date_default_timezone_set($timezone_name);
I'd encourage you to take a look at his answer in detail, since there's a bug that you might need to take into account.

EDITED (original answer completely scrapped)
One approach would be to create a map of offset hour strings (such as +0800) to a timezone name that PHP recognizes. It's not 1-to-1, since there are multiple names for each offset. But since you care about the offset and not the name, then your mapping can just choose any of the available timezone names for any given offset.
Since there aren't hundreds and hundreds of timezones, you really only end up with an array of about 35 entries (there are a few timezones at the 30-minute or even 45-minute mark).
Here is a code sample that pretty much does what you need:
$timezones = array(
'-1100' => 'Pacific/Midway',
'-1000' => 'US/Hawaii',
'-0900' => 'US/Alaska',
'-0800' => 'US/Pacific',
'-0700' => 'US/Arizona',
'-0600' => 'America/Mexico_City',
'-0500' => 'US/Eastern',
'-0430' => 'America/Caracas',
'-0400' => 'Canada/Atlantic',
'-0330' => 'Canada/Newfoundland',
'-0300' => 'America/Buenos_Aires',
'-0200' => 'Atlantic/Stanley',
'-0100' => 'Atlantic/Azores',
'-0100' => 'Atlantic/Cape_Verde',
'+0000' => 'Europe/London',
'+0100' => 'Europe/Amsterdam',
'+0200' => 'Europe/Athens',
'+0300' => 'Asia/Baghdad',
'+0330' => 'Asia/Tehran',
'+0400' => 'Europe/Moscow',
'+0430' => 'Asia/Kabul',
'+0500' => 'Asia/Karachi',
'+0530' => 'Asia/Kolkata',
'+0545' => 'Asia/Kathmandu',
'+0600' => 'Asia/Yekaterinburg',
'+0700' => 'Asia/Novosibirsk',
'+0800' => 'Asia/Krasnoyarsk',
'+0800' => 'Asia/Urumqi',
'+0900' => 'Asia/Irkutsk',
'+0930' => 'Australia/Adelaide',
'+1000' => 'Asia/Yakutsk',
'+1000' => 'Australia/Sydney',
'+1100' => 'Asia/Vladivostok',
'+1200' => 'Asia/Magadan'
);
for ($offset_hours = -11; $offset_hours <= 12; $offset_hours++) {
// Convert to a timezone string. For example, 8 => +0800
$offset_string = sprintf("%+03d", $offset_hours) . "00";
date_default_timezone_set($timezones[$offset_string]);
$dt = new DateTime();
print "OFFSET: $offset_hours hours ($offset_string)\n";
print $dt->format(DATE_RFC822) . "\n";
print "\n";
}
The for loop just demonstrates the setting of almost all the different timezones based on an iterating offset (I excluded the 30-minute and 45-minute mark timezones, for simplicity).
Here is an excerpt of the output from running the above code:
OFFSET: -11 hours (-1100)
Tue, 25 Feb 14 04:24:13 -1100
OFFSET: -10 hours (-1000)
Tue, 25 Feb 14 05:24:13 -1000
OFFSET: -9 hours (-0900)
Tue, 25 Feb 14 06:24:13 -0900
...
...
...
OFFSET: -1 hours (-0100)
Tue, 25 Feb 14 14:24:13 -0100
OFFSET: 0 hours (+0000)
Tue, 25 Feb 14 15:24:13 +0000
OFFSET: 1 hours (+0100)
Tue, 25 Feb 14 16:24:13 +0100
...
...
...
OFFSET: 10 hours (+1000)
Wed, 26 Feb 14 02:24:13 +1100
OFFSET: 11 hours (+1100)
Wed, 26 Feb 14 02:24:13 +1100
OFFSET: 12 hours (+1200)
Wed, 26 Feb 14 03:24:13 +1200
Credit goes to #Eugene Manuilov for his StackOverflow answer regarding PHP Timezones, as it meant I did not have to write up the array entirely from scratch.

Related

Converting date to timestamp based on timezone

I'm fetching emails using PHP Imap and this is an example of an object:
Array
(
[0] => stdClass Object
(
[subject] => Email Subject
[from] => Sender <sender#domain.com>
[to] => me#domain.com
[date] => Sat, 19 Aug 2017 20:09:33 +1000
[message_id] => <80d657c74967c8dc56138ca9552f0a2e#anyweb.apca.local>
[size] => 1881518
[uid] => 703
[msgno] => 527
[recent] => 0
[flagged] => 0
[answered] => 0
[deleted] => 0
[seen] => 0
[draft] => 0
[udate] => 1503137430
)
)
Although I do have a udate but I wanted to double check if matches my timezone, so I did:
date_default_timezone_set('Australia/Melbourne');
$str = 'Sat, 19 Aug 2017 20:09:33 +1000';
echo strtotime($str); // 1503137373 ??
Even tried:
$date = new DateTime($str, new DateTimeZone('Australia/Melbourne'));
$timestamp = $date->format('U');
echo $timestamp; // 1503137373 ??
So in both cases I get a timestamp that doesn't match the one thats fetched from the mail server, what am I missing here?
udate - labeled by a mailserver
Date, date - labeled by a client
The difference between 'date' and 'udate' seems to be rather more than
just the way they're formatted.
'date' is the date that was written in the headers by the sender's
mail client, and probably bears little to do with reality. It's
dependent on your sender knowing what the correct time is; it could be
out by a few minutes, days, months or even years.
'udate' is the real date that the e-mail hit your IMAP server.
Use 'udate' if you want to do neat stuff like work out how much e-mail
you get sent on a daily basis - or, as I do, how much spam I get.
http://titanic.fauser.edu/php/function.imap-headerinfo.php.htm
From here:
date - The message date as found in its headers
Date - Same as date
udate - mail message date in Unix time
you can do something like this
date_default_timezone_set('Australia/Melbourne');
$script_tz = date_default_timezone_get();
if (strcmp($script_tz, ini_get('date.timezone'))){
echo 'Script timezone differs from ini-set timezone.';
} else {
echo 'Script timezone and ini-set timezone match.';
}

PHP and DST conversion issue

I'm having trouble understanding how my code adapts to DST, as it's incorrect with the recent update. I'm storing a date time in the database based on UTC and then converting it back to the local timezone for display. If PHP is taking DST into account, something else is wrong because all of my stored dates are 1 hour off.
$stored_date = '2016-11-16 12:04:01'; // in UTC
$dateTime = new DateTime($stored_date, new DateTimeZone('UTC'));
$dateTimeZone = new DateTimeZone('America/New_York');
$dateTime->setTimezone($dateTimeZone);
print_r($dateTime);
Last week, before DST ended, this would have printed out 2016-11-16 08:04:01. This week, now that DST has ended, it prints out 2016-11-16 07:04:01. Why the hour difference if PHP is properly handing the DST shift?
It shouldn't matter the server settings (I don't think) because I'm explicitly doing the conversion within PHP, right?
I'm ready to start doing a check with PHP to see if DST is in effect and offsetting the conversion by 1 hour because I can't figure out why that hour isn't being automatically compensated for within the DateTime class.
New York city switches between these time zones:
Winter: EST (Eastern Standard Time) = UTC -5
Summer: EDT (Eastern Daylight Time) = UTC -4
According to timeanddate.com the switch will happen on 6th of November. Thus the result is correct: 12 - 5 = 7
In other words, PHP is perfectly aware of DST, as we can see in the following code:
$dateTime = new DateTime('2016-11-05 12:04:01', new DateTimeZone('UTC'));
$dateTime->setTimezone(new DateTimeZone('America/New_York'));
echo $dateTime->format('r') . PHP_EOL;
$dateTime = new DateTime('2016-11-06 12:04:01', new DateTimeZone('UTC'));
$dateTime->setTimezone(new DateTimeZone('America/New_York'));
echo $dateTime->format('r') . PHP_EOL;
Sat, 05 Nov 2016 08:04:01 -0400
Sun, 06 Nov 2016 07:04:01 -0500
You can inspect the exact information available in your system's time database:
$timeZone = new DateTimeZone('America/New_York');
print_r($timeZone->getTransitions(mktime(0, 0, 0, 1, 1, 2016), mktime(0, 0, 0, 12, 31, 2016)));
Array
(
[0] => Array
(
[ts] => 1451602800
[time] => 2015-12-31T23:00:00+0000
[offset] => -18000
[isdst] =>
[abbr] => EST
)
[1] => Array
(
[ts] => 1457852400
[time] => 2016-03-13T07:00:00+0000
[offset] => -14400
[isdst] => 1
[abbr] => EDT
)
[2] => Array
(
[ts] => 1478412000
[time] => 2016-11-06T06:00:00+0000
[offset] => -18000
[isdst] =>
[abbr] => EST
)
)

php strtotime some day un complete

I have some date, like:
20 November 06:10
12 November 08:12
10 October 13:23
There all in the past 6 months, Now I want to strtotime() them, but they are all un complete (lack of year), how to make some process so that I could strtotime() them?
Try this:
$dates = array("10 October 13:23", "12 November 08:12", "10 October 13:23");
foreach($dates as $d){
$exploded = explode(" ", $d);
$newDate = array_slice($exploded, 0,2,true)+array(2=>"2012")+array(3 => $exploded[2]);
//print_r($newDate);
$time = strtotime(implode($newDate));
echo $time."<br/>";
}
The output i got is:
1349868180
1352704320
1349868180
The logic is:
You lack the year, so I exploded the dates into an array to slice them, insert the year (the +array(2=>"2012") part) and glue them again with implode, and then run the strtotime.
This work only for this year, so you can use this logic to add the year to all your dates, or in the future there will be absolutely no way to filter dates from different years.
I added the dates into an array for loop through all of them, you can use the loop other ways, depending on where you have all your dates stored. For example if they are in a database you can include the script in the while($row = mysqli_fetch_assoc($result)) part where $d would be $row['date'] instead.
You should use the DateTime class and its createFromFormat and getTimeStamp methods instead of strtotime.
print_r(date_parse_from_format("d F H:i", '20 November 06:10'));
gives you:
Array
(
[year] =>
[month] => 11
[day] => 20
[hour] => 6
[minute] => 10
[second] => 0
[fraction] =>
[warning_count] => 0
[warnings] => Array
(
)
[error_count] => 0
[errors] => Array
(
)
[is_localtime] =>
)

Countdown to sunday

I am currently looking at creating a script for my site that will count down to sunday of that week, every week.
Example:
The user visits the site on a saturday at 11:30am, they will be greeted with:
"The next time this page will be updated is in 0 days, 12 hours and 30 minutes."
Any ideas?
You can use this little trick to get a timestamp for midnight next Sunday:
$sunday = strtotime('next Sunday');
See this answer for how to format it into something useful. Right now I get this:
print_r(dateDifference($sunday, time()));
Array
(
[years] => 0
[months_total] => 0
[months] => 0
[days_total] => 0
[days] => 0
[hours_total] => 4
[hours] => 4
[minutes_total] => 256
[minutes] => 16
[seconds_total] => 15387
[seconds] => 27
)
I am using similar to this solution in one of my pojects. You can use it like this:
ago(strtotime("next sunday")) but you need to change $difference = $now - $time; to $difference = $time - $now;
Here's one solution:
http://jsfiddle.net/foxbunny/xBE7L/
It also automatically updates every second.
Edit: I've included the offset parameter, and you use it to supply the difference between user's and server's time-zone if necessary.

store monthly date into array from the selected date

Assume that selected date from Canlender is 02/09/2011. To store weekly date into array from 20/09/2011 is
for($i=0; $i<7; $i++)
{
$WeeklyDate[] = date("Y-m-d", strtotime(2011-09-02) - 86400*$i);
}
My question is how to store monthly date into array from the selected date.
Many thanks
---Update----------
The final result of monthlyDate should look like the following:
$monthlyDate= array{2011-08-03, 2011-08-04, 2011-08-05, 2011-08-06, 2011-08-07 ....2011-08-31, 2011-09-01, 2011-09-02}
First, calculate the number of days in a month using cal_days_in_month and then proceed as you are doing with weeks eg:
$days = cal_days_in_month(CAL_GREGORIAN, 9, 2011);
for($i = 0; $i <= $days; $i++)
{
$MonthlyDate[] = date("Y-m-d", strtotime(2011-09-02) - 86400*$i);
}
Notice that CAL_GREGORIAN is a built-in constant.
Working Example
Whenever programs are incrementing a date using 86400 there is a risk of unexpected output because of DST.
By using strtotime() with a unit larger than hours (like days, weeks, months, etc.) preventing any DST hiccups. Note: a DateTime object approach can be used but for this case, it is unnecessary overhead.
The following is an adjusted form of a one-liner date range function I developed.
Here is the online demo for this case.
function getDatesFromRange($a,$b,$x=0,$dates=[]){
while(end($dates)!=$b && $x=array_push($dates,date("Y-m-d",strtotime("$a +$x day"))));
return $dates;
}
$date='2011-09-02';
$monthlyDate=getDatesFromRange(date("Y-m-d",strtotime("$date -1 month +1 day")),$date);
var_export($monthlyDate);
output as desired/expected:
array (
0 => '2011-08-03',
1 => '2011-08-04',
2 => '2011-08-05',
3 => '2011-08-06',
4 => '2011-08-07',
5 => '2011-08-08',
6 => '2011-08-09',
7 => '2011-08-10',
8 => '2011-08-11',
9 => '2011-08-12',
10 => '2011-08-13',
11 => '2011-08-14',
12 => '2011-08-15',
13 => '2011-08-16',
14 => '2011-08-17',
15 => '2011-08-18',
16 => '2011-08-19',
17 => '2011-08-20',
18 => '2011-08-21',
19 => '2011-08-22',
20 => '2011-08-23',
21 => '2011-08-24',
22 => '2011-08-25',
23 => '2011-08-26',
24 => '2011-08-27',
25 => '2011-08-28',
26 => '2011-08-29',
27 => '2011-08-30',
28 => '2011-08-31',
29 => '2011-09-01',
30 => '2011-09-02',
)

Categories