PHP Daylight Savings Time Issue - php

I have the following function that is used to get the correct time and is used for my website.
Unfortunately, It doesn't check for daylight savings time and is causing a time error. I was wondering if anyone would have a possible solution or help me out with my issue?
I am very new to PHP coding and am still getting my footing with it. It would be really helpful if someone would be able to help me with a way to have it automatically make the switch for in the future.
Here is the function:
function getDSTDifference() {
$timezone = date_default_timezone_get();
$newTZ = new DateTimeZone($timezone);
$trans = $newTZ->getTransitions();
$offset = $trans[0]['offset'] /60 /60;
return $offeset;
}
---EDIT---
To better clarify what I am trying to say:
I have a website that monitors call times. These call times become off by an hour during the switch between DST and Regular Time (and vice versa). For example this past weekend caused times to show up like this: -57:01:23. Instead of have the function the way that it is - where I would have to manually go in and uncomment/comment out the two lines of code every time DST and Regular Time switch, is there a possible solution to making the function be able to do this automatically? Kinda like a more permanent solution. To me it just seems like redundant coding to have to constantly revisit that function to make what seems like a simple change over and over again. Again, I have not done a lot of work with PHP code before, therefore I am not familiar with built in functions that can be used or if I would have to create this on my own. If anybody would have some information/help to go about this, it would be much appreciated.
function getDSTDifference() {
$timezone = date_default_timezone_get();
$NewTZ = new DateTimeZone($timezone);
$transition = $NewTZ->getTransitions();
$offset = $transition[0]['offset'] /60 /60;
//The following two lines need to be commented out when it is
//daylight savings time
//They need to be uncommented when Daylight Savings Time ends
$dst = $transition[0]['isdst'];
$offset = $offset - $dst;
return $offset;
}
Hopefully that makes more sense!
Thanks in advance for the help!

One possibility to calculate the difference between two given hours is to use DateTime::diff(). At the time of writing this answer there was a PHP bug and you had to convert to UTC before:
<?php
$zone = new DateTimeZone('Europe/Madrid');
$start = new DateTime('2013-03-31 1:59:00 ', $zone);
$end = new DateTime('2013-03-31 3:00:00', $zone);
// Workaround for bug #63953
// No longer required since PHP/5.6.0, PHP/5.5.8 or PHP/5.4.24
$start->setTimeZone(new DateTimeZone('UTC'));
$end->setTimeZone(new DateTimeZone('UTC'));
$difference = $start->diff($end);
echo $difference->format('%H:%I:%S');
... prints 00:01:00 because that's when DST started in Western Europe.
You can also use Unix timestamps, which represent a fixed moment in time thus do not depend on time zones:
<?php
$zone = new DateTimeZone('Europe/Madrid');
$start = new DateTime('2013-03-31 1:59:00 ', $zone);
$end = new DateTime('2013-03-31 3:00:00', $zone);
$difference = $end->format('U') - $start->format('U');
echo "$difference seconds";
... prints 60 seconds.
Edit #1: What do you mean? My snippet contains sample data so you can test it—in real code you'll use your real data. You can (and should) set the correct time zone as default so you don't need to specify it every time. But even if you don't, the server's time zone will never change—even if you decide to physically move the computer to another state or country several times a year you can still opt for a fixed time zone of your choice (your app's time zone can be different from your server's).
If you really want a solution that requires you to change the code manually twice a year (for whatever the reason, maybe to charge maintenance fees), you'd better skip date/time functions and use strings; otherwise you'll risk PHP doing the calculations for you.
Edit #2:
$start and $end represent your data. I chose sample data one minute before DST just to illustrate that the diff code works fine. It's the same as when you see <?php echo "Hello, World!"; ?> in a PHP tutorial: Hello, World! is sample data to illustrate how echo works but you don't have to use Hello, World! when you write your app.
To convert from GMT to EST with PHP you create a date that belongs to GMT:
$date = new DateTime('14:30', new DateTimeZone('GMT'));
echo 'GMT: ' . $date->format('r') . PHP_EOL;
// GMT: Thu, 07 Nov 2013 14:30:00 +0000
... and then switch to EST:
$date->setTimeZone(new DateTimeZone('EST'));
echo 'EST: ' . $date->format('r') . PHP_EOL;
// EST: Thu, 07 Nov 2013 09:30:00 -0500
However, if your original dates are in GMT, converting them to EST before substracting them does not provide any benefit.

Related

Automatically calculate is daylight savings from timezone offset in php [duplicate]

I need to send an email to users based wherever in the world at 9:00 am local time. The server is in the UK. What I can do is set up a time difference between each user and the server's time, which would then perfectly work if DST didn't exist.
Here's an example to illustrate it:
John works in New York, -5 hours from the server (UK) time
Richard works in London, UK, so 0 hour difference with the server.
When the server goes from GMT to GMT +1 (BST) at 2:00am on a certain Sunday, this means that John now has a -6H time difference now.
This scenario I can still handle by updating all the users outside the server's local time, but once I've moved forward/backward the time of all the other users, I still need a way to detect when (time and date) the users living outside the UK will (or will not) change their local time to a probable DST one.
I need a PHP method to know/detect when other parts of the world will enter/exit DST.
Do you need to know all the details of DST transition yourself? or do you just need to know when is 9:00 am in a given timezone?
If it's the latter, PHP can use your operating system's timezone database to do that for you. The strtotime() function is remarkably good at "figuring out" what you mean:
echo strtotime("today 9:00 am America/New_York"); // prints "1306501200"
echo strtotime("today 9:00 am Europe/London"); // prints "1306483200"
Just make sure you're using one of the PHP supported timezones.
As Jimmy points out you can use timezone transitions, but this is not available on PHP <5.3. as dateTimeZone() is PHP>=5.2.2 but getTransitions() with arguments is not! In that case here is a function that can give you timezone data, including whether in DST or not.
function timezonez($timezone = 'Europe/London'){
$tz = new DateTimeZone($timezone);
$transitions = $tz->getTransitions();
if (is_array($transitions)){
foreach ($transitions as $k => $t){
// look for current year
if (substr($t['time'],0,4) == date('Y')){
$trans = $t;
break;
}
}
}
return (isset($trans)) ? $trans : false;
}
Having said that, there is a simpler method using date() if you just need to know whether a timezone is in DST. For example if you want to know if UK is in DST you can do this:
date_default_timezone_set('Europe/London');
$bool = date('I'); // this will be 1 in DST or else 0
... or supply a timestamp as a second arg to date() if you want to specify a datetime other than your current server time.
Changing my answer a bit: DateTimeZone::getTransitions looks like it will do what you need, provided you have PHP >= 5.2.
From a comment in the documentation:
<?php
$theTime = time(); // specific date/time we're checking, in epoch seconds.
$tz = new DateTimeZone('America/Los_Angeles');
$transition = $tz->getTransitions($theTime, $theTime);
// only one array should be returned into $transition. Now get the data:
$offset = $transition[0]['offset'];
$abbr = $transition[0]['abbr'];
?>
So here, all we need to do is pass in the timezone we want to check and we can know if that timezone is in DST/what the offset is. You'll then need to check the offset against GMT to see if you want to send your e-mail now, or not now.
I created this PHP function to see if a time value is BST or not:
<?php
function isBST($timestamp)
{
$baseDate = date("m/d/Y H:i:s", $timestamp);
$clocktime=strtotime($baseDate." Europe/London")."\n";
$utctime=strtotime($baseDate." UTC")."\n";
//echo "$a \n$b \n";
if ($clocktime!=$utctime)
{
return true;
}
else
{
return false;
}
}
?>

php mktime gives different results for same date?

the following code:
// settings 1st alternative
$season = 2011;
$startweek = 36;
// calculation, 1st alternative
// in any case Jan 1st is in 1. week
$firstweekuniversal = mktime(0,0,0,1,4,$season);
// I'd like to calculate monday 0:00 of the given week.
// date(...) computes to monday of the calculated week.
$startday1 = $firstweekuniversal + 86400 * (7*($startweek-1) - date('w', $firstweekuniversal)+1);
// settings 2nd alternative
$StartingDate = "KW36 - 5.09.2011 (Mo)";
// calculation, 2nd alternative
$firstparts = explode(" ", $StartingDate);
$secparts = explode(".", $firstparts[2]);
$startday2 = mktime(0,0,0,intval($secparts[1]),intval($secparts[0]),intval($secparts[2]));
// Output
echo "Start: \$startday1=".$startday1.", Timestamp=\"".date("d.m.Y - H:i:s", $startday1)."\"<br>\n";
echo "Start: \$startday2=".$startday2.", Timestamp=\"".date("d.m.Y - H:i:s", $startday2)."\"<br>\n";
leads to this output:
Start: $startday1=1315177200, Timestamp="05.09.2011 - 01:00:00"
Start: $startday2=1315173600, Timestamp="05.09.2011 - 00:00:00"
where is the 1h difference coming from? It can't be summertime issues, in that case the 1h had to be the other way round, or am I wrong?
There's no difference, when I'm trying with my environment (localhost), but on the server of my prvider there is.
Can anyone explain this to me? I'm totally puzzled here.
Thanks in advance,
My understanding of daylight savings is that at a certain point between winter and summer the sun is starting to rise earlier, so at 8am it would feel like it should be 9am ... and that's exactly what the clock does, it moves forward making it later (by one hour typically). This is also why it's such a good excuse for being late at school :)
In any case, the timezone setting of your local php is probably UTC (or some other "neutral" timezone) but the server timezone setting honors daylight savings.

Understanding date processing with strtotime

I'm trying to get my head round someone else's code which they've written for handling the dates of when news stories are published. The problem has come up because they are using this line -
$date = strtotime("midnight", strtotime($dateString));
to process a date selected using a jquery calendar widget. This works fine for future dates, but when you try to use a date which is in the previous calendar year, it uses the current year instead. I think this is due to "midnight" finding the closest instance of the selected day and month.
I could remove the "midnight", but I'm not sure what the repercussions of this would be - is there a reason that the midnight could be there?
EDIT: this is the full block of code which handles the date. The date contains the time, which allows the user to publish an item at a specific time.
$array['display_date'] = '24 October, 2011 17:30';
$string = $array['display_date'];
$dateString = substr($string, 0, -5);
$timeArray = explode(':', substr($string, -5));
$hours_in_secs = 60 * 60 * $timeArray[0];
$mins_in_secs = $timeArray[1];
$date = strtotime("midnight", strtotime($dateString));
$timestamp = $date + $hours_in_secs + $mins_in_secs;
//assign timestamp to validation array
$array['display_date'] = $timestamp;
echo $array['display_date']; // Output = 1351094430 (Oct 24 2012 17:00:30)
This really depends on what $dateString contains. Assuming your jQuery widget delivered the time portion as well, your colleague likely wanted to remove the time portion. Compare the following:
echo date(DATE_ATOM, strtotime('2010-10-01 17:32:00'));
// 2010-10-01T17:32:00+02:00
echo date(DATE_ATOM, strtotime("midnight", strtotime('2010-10-01 17:32:00')));
// 2010-10-01T00:00:00+02:00
If your widget doesnt return the time portion, I dont see any reason for setting the date to midnight, because it will be midnight automatically:
echo date(DATE_ATOM, strtotime('2010-10-01'));
// 2010-10-01T00:00:00+02:00
Note that all these are dates in the past and they will result in the given year in the past, not the current year like you say. If they do in your code, the cause must be somewhere else.
Will there be repercussions when you change the code? We cannot know. This is just one line of code and we have no idea of any context. Your unit-tests should tell you when something breaks when you change code.
EDIT after update
The codeblock you show makes no sense whatsoever. Ask the guy who wrote it what it is supposed to do. Not only will it falsely return the current year for past years, but it will also give incorrect results for the minutes, e.g.
24 March, 2010 17:30 will be 2012-03-24T17:00:30+01:00
I assume this was an attempt at turning 24 March, 2010 17:30 into a valid timestamp, which is in a format strtotime does not recognize. But the approach is broken. When you are on PHP5.3 use
$dt = DateTime::createFromFormat('d F, Y H:i', '24 March, 2010 17:30');
echo $dt->format(DATE_ATOM); // 2010-03-24T17:30:00+01:00
If you are not on 5.3 yet, go through https://stackoverflow.com/search?q=createFromFormat+php for alternate solutions. There is a couple in there.

PHP daylight saving time detection

I need to send an email to users based wherever in the world at 9:00 am local time. The server is in the UK. What I can do is set up a time difference between each user and the server's time, which would then perfectly work if DST didn't exist.
Here's an example to illustrate it:
John works in New York, -5 hours from the server (UK) time
Richard works in London, UK, so 0 hour difference with the server.
When the server goes from GMT to GMT +1 (BST) at 2:00am on a certain Sunday, this means that John now has a -6H time difference now.
This scenario I can still handle by updating all the users outside the server's local time, but once I've moved forward/backward the time of all the other users, I still need a way to detect when (time and date) the users living outside the UK will (or will not) change their local time to a probable DST one.
I need a PHP method to know/detect when other parts of the world will enter/exit DST.
Do you need to know all the details of DST transition yourself? or do you just need to know when is 9:00 am in a given timezone?
If it's the latter, PHP can use your operating system's timezone database to do that for you. The strtotime() function is remarkably good at "figuring out" what you mean:
echo strtotime("today 9:00 am America/New_York"); // prints "1306501200"
echo strtotime("today 9:00 am Europe/London"); // prints "1306483200"
Just make sure you're using one of the PHP supported timezones.
As Jimmy points out you can use timezone transitions, but this is not available on PHP <5.3. as dateTimeZone() is PHP>=5.2.2 but getTransitions() with arguments is not! In that case here is a function that can give you timezone data, including whether in DST or not.
function timezonez($timezone = 'Europe/London'){
$tz = new DateTimeZone($timezone);
$transitions = $tz->getTransitions();
if (is_array($transitions)){
foreach ($transitions as $k => $t){
// look for current year
if (substr($t['time'],0,4) == date('Y')){
$trans = $t;
break;
}
}
}
return (isset($trans)) ? $trans : false;
}
Having said that, there is a simpler method using date() if you just need to know whether a timezone is in DST. For example if you want to know if UK is in DST you can do this:
date_default_timezone_set('Europe/London');
$bool = date('I'); // this will be 1 in DST or else 0
... or supply a timestamp as a second arg to date() if you want to specify a datetime other than your current server time.
Changing my answer a bit: DateTimeZone::getTransitions looks like it will do what you need, provided you have PHP >= 5.2.
From a comment in the documentation:
<?php
$theTime = time(); // specific date/time we're checking, in epoch seconds.
$tz = new DateTimeZone('America/Los_Angeles');
$transition = $tz->getTransitions($theTime, $theTime);
// only one array should be returned into $transition. Now get the data:
$offset = $transition[0]['offset'];
$abbr = $transition[0]['abbr'];
?>
So here, all we need to do is pass in the timezone we want to check and we can know if that timezone is in DST/what the offset is. You'll then need to check the offset against GMT to see if you want to send your e-mail now, or not now.
I created this PHP function to see if a time value is BST or not:
<?php
function isBST($timestamp)
{
$baseDate = date("m/d/Y H:i:s", $timestamp);
$clocktime=strtotime($baseDate." Europe/London")."\n";
$utctime=strtotime($baseDate." UTC")."\n";
//echo "$a \n$b \n";
if ($clocktime!=$utctime)
{
return true;
}
else
{
return false;
}
}
?>

Displaying timezones without using UTC

I've been reading up on how to do this all morning, and the general consensus is this: store your timestamps in UTC and calculate the offset from there.
However, like most people, I am maintaining an existing codebase and I can't change the way we store our dates. At least, not in the allotted time frame.
So my question is, can I safely do something like this?
// this is my time zone
$timestamp = strtotime($timestampFromDatabase);
date_default_timezone_set('America/New York');
$tz = date_default_timezone_get();
// this is theoretically their timezone, which will eventually
// be taken from a session variable
date_default_timezone_set('Europe/Paris');
$offset = time() - $timestamp;
$timestamp -= $offset;
// now that I have the offset, display dates like this
date('r', $timestamp);
Some people have advised against doing date arithmetic, but is what I'm doing here wrong? Is there a better way? Are there pitfalls I need to watch out for? I'm particularly interested in what kind of bugs this method could produce, and if any, who will they affect?
My boss is the kind of guy that doesn't care if the bug only affects 1% of the user base.
Edit: Can I make this any clearer? I don't seem to have too many takers on this. Anyone? Even a vague suggestion or a link would be immensely helpful.
The short answer is that timezones are tricky to work with and no-one wants to tackle the difficult questions!
The longer, answer is this:
Firstly, you need to create a representation of your date, in their current timezones. The following are equivalent:
date_default_timezone_set('America/New York');
$date = new DateTime(null);
OR
$date = new DateTime(null, new DateTimeZone('America/New York'));
This gives you a date with zone set to America/New York. We can easily convert it to the user timezone, in this case Europe/Paris with the next line:
$date->setTimezone(new DateTimeZone('Europe/London'));
You can use the following line to get the date/time represented at any point.
echo $date->format('d/m/Y H:i:s');
I strongly recommend this over arithmetic, as the built in PHP functions have knowledge of daylight saving times and lots of complicated things that you won't get right.
My test script in full:
date_default_timezone_set('America/Belize');
$date = new DateTime(null);
echo $date->format('d/m/Y H:i:s') . '<br>';
$date->setTimezone(new DateTimeZone('Europe/London'));
echo $date->format('d/m/Y H:i:s') . '<br>';

Categories