time() and date() problems after time change (DST - standard) - php

In PHP I would like to output an HTML option list containing dates for the next 14 days.
These appointments are always at 18 o'clock:
$today_day = date('d');
$today_month = date('m');
$today_year = date('Y');
$date_entry = mktime(18, 00, 00, $today_month, $today_day, $today_year);
$optionsStr = '<select name="date">';
for ($d = 1; $d < 14; $d++) {
$date_entry_temp = $date_entry+86400*$d;
$optionsStr .= '<option value="'.$date_entry_temp.'">'.date('d.m.Y', $date_entry_temp).'</option>';
}
$optionsStr .= '</select>';
echo $optionsStr;
The user can then choose from one of these dates and submit the form. The chosen timestamp is then inserted into the database.
So I have some entries in my database.
On another page there is a list of current appointments:
mysql_query("SELECT id, name FROM appointments WHERE date_time = ".time());
So at 18 o'clock there should be some output as there are entries in the database for that day. This works perfectly good until the time changes from DST to standard time or vice versa. Then, indeed, is wrong:
The appointments are shown one hour too late or too early respectively.
How can I solve this problem?

mktime() creates a unix timestamp. A unix time stamp is the number of seconds from January 1, 1970, 00:00:00 GMT +0000. (Greenwich time)
When you set your timezone to "Europe/Berlin", the timezone is either GMT+0100 (in winter) or GMT+0200 (in summer). This means that the Greenwich time of your appointments changes by one hour when you have DST. That means that the time between the first appointment before the change and the next appointment after the change is not 24 hours, but 23 or 25. However, you generate the appointments by adding 86400 seconds = 24 hours.
You can use the DateTime object and the add() method instead. It takes DST changes into account.
// create a new date object with todays date
$date = new DateTime();
// set the time to 18:00
$date->setTime(18,0,0);
$optionsStr = '<select name="date">';
for ($i = 0; $i < 14; $i++) {
// add 1 day
$date->add(new DateInterval('P1D'));
$optionsStr .= '<option value="'.$date->format('U').'">'.$date->format('d.m.Y').'</option>';
}
$optionsStr .= '</select>';
echo $optionsStr;
See http://www.php.net/manual/en/datetime.add.php for more information.

Your main issue is that you're not working on GMT dates. Here's a colorful post that highlights the resulting pitfalls:
http://derickrethans.nl/storing-date-time-in-database.html
What you should be doing is, store your appointments datetimes at time zone UTC, compare datetimes at time zone UTC, and display datetimes to users in your (ok) or their (ideal) preferred time zone.

Seems you have a problem with timezones: http://dev.mysql.com/doc/refman/5.0/en/timestamp.html
I would get the time selected, convert from your local time to UTC and then put it in the database. If you read it out, convert back from UTC to your localtime.

When relying on the time, please make sure that you always SET A TIMEZONE, either in your php.ini or in your code. And there's not a whole lot you can do about your database entries, if you sort them by time or date they will end up being interleaved due to the new date being before the date of your last entry prior to the timechange.

Related

Add -1 day to DBDatetime with a specific timestamp in php

lets assume that NOW is 2021-09-15 (DBDatetime format). I would like to add to that existing date -1 day and set time to be at 10:00 AM, so:
Initial date would be: 2021-09-15
Expired date would be: 2021-09-16 10:00 AM
The reason for that is I would like to get videos (on my newbie site) even after their airing time exired. So lets say expiring time is set to initial date and i want to have that till 21-09-16 10:00 AM is up.
Is that possible?
Yes, it's very easy with DateTime::modify.
$basisDate = '2021-09-15'; //or 'today' for the current day
$date = date_create($basisDate)->modify('+1 Day 10:00');
//test output
echo $date->format("Y-m-d H:i"); //2021-09-16 10:00
Another variant, not so tricky and easier to understand:
$date = date_create($basisDate)
->modify('+1 Day')
->setTime(10,0)
;

need to store date as per user timezone in php

i have simple php script where i have this variable
$date = date('Y-m-d', time());
The Problem: The variable is storing date as per my server timezone.
What is want: I want to store date as per user time zone, take a look into
example below:
1- tom checkin from USA
2- jenne checkin from Asia
since there is 12 hrs. difference so the date will be different too sometime
here is found some example but it's not dynamic
Converting GMT time to local time using timezone offset in php
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);
Now what exactly i am looking,
1- in case of TOM $date should be 18
11:38 PM
Tuesday, 18 April 2017 (GMT-5)
Time in Chicago, IL, USA
2- in case of jenne $date should be 19
9:40 AM
Wednesday, 19 April 2017 (GMT+5)
Time in Lahore
difficult writing code in the comments section so i posted a working answer for you here
<?php
// here $usertimezone should be set = to what you have in your database
$usertimezone="Asia/Shanghai";
date_default_timezone_set('"'.$usertimezone.'"');
//new date and time
$ndate= new datetime();
//split into date and time seperate
$nndate =$ndate->format("Y-m-d");
$nntime= $ndate->format("H:i:S");
//here you can test it
echo $nndate;
echo $nntime;
?>
Use this function date_default_timezone_set for setting timezone, From this function you can set the timezone according to user and then get the required format.
Examples
<?php
date_default_timezone_set("new/timezone");//set the name of timezone here example Asia/Kokata
echo $date= date("Y-m-d H:i:s");

How to return timestamp based on user timezone?

Say that I have a timezone variable which is returned from a database that my users can set to their prefered time zone. GMT+5 would return simply as:
$timezone = 5;
I need the users timezone so that I can reflect the date of an event in their particular time format. Here is what I have so far:
while ($row = mysqli_fetch_array($query, MYSQLI_ASSOC)) {
$time = strftime("%Y-%m-%d %I:%M %p", strtotime($row["time"]));
}
echo $time;
How could I change the above output to reflect the time of the event based on the users preferred time zone?
//Output = 2017-01-15 12:09 AM
//Expected Output: 2017-01-14 07:09 PM
Use the DateTime and DateTimeZone objects.
<?php
$storedDate = new DateTime('2017-01-15 12:09 AM');
$userDate = (clone $storedDate)->setTimeZone(new DateTimeZone('Europe/Paris'));
printf(
"Stored Date:\t%s\nUser Date:\t%s\n",
$storedDate->format(DATE_ATOM),
$userDate->format(DATE_ATOM)
);
// outputs:
// Stored Date: 2017-01-15T00:09:00+00:00
// User Date: 2017-01-15T01:09:00+01:00
As said in my comment, I dot not advise to store the timezone as an integer.
First, because some timezone can't fit an integer, for example India is up to 5:30 (+0530) from the UTC time.
Storing as integer also won't help you to properly handle summer time in some countries, or even in some region in a same country (see Brazil).
Timezone offset are subject to change, but defined configuration for a given location such as Europe/Paris are likely not.

Server Time - driving me nuts :-)

Okay so I have a server in Denver with a user in New Zealand. I know everything about the user (timezone etc) and through the program they request something to happen in advance - let's say at 11:30am on August 5th 2013. I have a CRON job that runs every 15 minutes and asks the database if any requests are pending for the next 15 minute period, but how do I convert their stored time to the servers equivalent.
I set the default timezone for calculations: date_default_timezone_set('America/Denver')
I take the time now on the server and turn it into epoch: strtotime(date('Y-m-d H:i:s'))
I add the 15 minutes to create a range: $forward15 = strtotime('now +15 minutes')
I get the user chosen date from the database (and their timezone): 2013-08-05 11:30:00
Now what? If I convert that into epoch, it'll just be the servers version of that date.
NEW SOLUTION SEE BELOW!
If you know the timezone you can simply "add" it to your time.
For example:
server time: 01/01/01 00:00
the time the user wants: 01/01/01 01:00
the timezone of the user: GMT - 5
Just get the time (01/01/01 01:00) and add +5 => 01/01/01 06:00
So: your script needs to be executed at 01/01/01 06:00
(convert to timestamp where needed)
Added a little php to demonstrate
<?php
$servertime = time(); //timestamp of 01/01/01 00:00
$usertime = "01/01/01 06:00";//database
$userUTC = "-5";//database
strreplace($userUTC, "-", "+";
$replacetext = $userUTC . " days";
$usertime = strtotime($replacetext, $usertime);//now usertime is in your local timezone
$crontime = date("d/m/Y H:i");//the time you want the script to be executed
?>
I'm just assuming that the timezone is saved as "-5" for example and not Europe/Amsterdam Just tell me if i'm wrong.
edit 14:37
This could be a even better solution i think!
<?php
$usertime = "01/01/01 06:00";
$userUTC = "-5";
$userdate = $usertime . " " . $userUTC;
$usertimestamp = strtotime($userdate);//now you have the timestamp with correct timezone
$crontime = date("d/m/Y H:i", $usertimestamp);//formatted to the right date
echo $crontime;
?>
Edit: 25-07-2013 14:26
New solution to suit your database:
<?php
$usertime = "01/01/01 06:00";
$userUTC = "Pacific/Auckland";//get from database
$userdate = $usertime . " " . $userUTC;
$usertimestamp = strtotime($userdate);//now you have the timestamp with correct timezone
$crontime = date("d/m/Y H:i", $usertimestamp);//formatted to the right date
echo $crontime;
?>
the server is in which GMT time zone here is extremely easy way to get time and date for any time zone. This is done with time() and gmdate() function. gmdate() function normally give us GMT time but by doing a trick with time() function we can get GMT+N or GMT-N means we can get time for any GMT time zone.
For example you have to get time for GMT+5 we will do it like following
<?php
$offset=5*60*60; //converting 5 hours to seconds.
$dateFormat="d-m-Y H:i";
$timeNdate=gmdate($dateFormat, time()+$offset);
?>
Now if you have to get the time which is GMT-5 now we will just subtract the offset from the time() instead of adding into time like in following example we are getting time for GMT-4
<?php
$offset=4*60*60; //converting 5 hours to seconds.
$dateFormat="d-m-Y H:i";
$timeNdate=gmdate($dateFormat, time()-$offset);
?>

Can't get previous month from DateTime in PHP- Is this a (pretty big) bug?

I need to create functions in PHP that let me step up/down given datetime units. Specifically, I need to be able to move to the next/previous month from the current one.
I thought I could do this using DateTime::add/sub(P1M). However, when trying to get the previous month, it messes up if the date value = 31- looks like it's actually trying to count back 30 days instead of decrementing the month value!:
$prevMonth = new DateTime('2010-12-31');
Try to decrement the month:
$prevMonth->sub(new DateInterval('P1M')); // = '2010-12-01'
$prevMonth->add(DateInterval::createFromDateString('-1 month')); // = '2010-12-01'
$prevMonth->sub(DateInterval::createFromDateString('+1 month')); // = '2010-12-01'
$prevMonth->add(DateInterval::createFromDateString('previous month')); // = '2010-12-01'
This certainly seems like the wrong behavior. Anyone have any insight?
Thanks-
NOTE: PHP version 5.3.3
(Credit actually belongs to Alex for pointing this out in the comments)
The problem is not a PHP one but a GNU one, as outlined here:
Relative items in date strings
The key here is differentiating between the concept of 'this date last month', which, because months are 'fuzzy units' with different numbers of dates, is impossible to define for a date like Dec 31 (because Nov 31 doesn't exist), and the concept of 'last month, irrespective of date'.
If all we're interested in is the previous month, the only way to gaurantee a proper DateInterval calculation is to reset the date value to the 1st, or some other number that every month will have.
What really strikes me is how undocumented this issue is, in PHP and elsewhere- considering how much date-dependent software it's probably affecting.
Here's a safe way to handle it:
/*
Handles month/year increment calculations in a safe way,
avoiding the pitfall of 'fuzzy' month units.
Returns a DateTime object with incremented month/year values, and a date value == 1.
*/
function incrementDate($startDate, $monthIncrement = 0, $yearIncrement = 0) {
$startingTimeStamp = $startDate->getTimestamp();
// Get the month value of the given date:
$monthString = date('Y-m', $startingTimeStamp);
// Create a date string corresponding to the 1st of the give month,
// making it safe for monthly/yearly calculations:
$safeDateString = "first day of $monthString";
// Increment date by given month/year increments:
$incrementedDateString = "$safeDateString $monthIncrement month $yearIncrement year";
$newTimeStamp = strtotime($incrementedDateString);
$newDate = DateTime::createFromFormat('U', $newTimeStamp);
return $newDate;
}
Easiest way to achieve this in my opinion is using mktime.
Like this:
$date = mktime(0,0,0,date('m')-1,date('d'),date('Y'));
echo date('d-m-Y', $date);
Greetz Michael
p.s mktime documentation can be found here: http://nl2.php.net/mktime
You could go old school on it and just use the date and strtotime functions.
$date = '2010-12-31';
$monthOnly = date('Y-m', strtotime($date));
$previousMonth = date('Y-m-d', strtotime($monthOnly . ' -1 month'));
(This maybe should be a comment but it's to long for one)
Here is how it works on windows 7 Apache 2.2.15 with PHP 5.3.3:
<?php $dt = new DateTime('2010-12-31');
$dt->sub(new DateInterval('P1M'));
print $dt->format('Y-m-d').'<br>';
$dt->add(DateInterval::createFromDateString('-1 month'));
print $dt->format('Y-m-d').'<br>';
$dt->sub(DateInterval::createFromDateString('+1 month'));
print $dt->format('Y-m-d').'<br>';
$dt->add(DateInterval::createFromDateString('previous month'));
print $dt->format('Y-m-d').'<br>'; ?>
2010-12-01
2010-11-01
2010-10-01
2010-09-01
So this does seem to confirm it's related to the GNU above.
Note: IMO the code below works as expected.
$dt->sub(new DateInterval('P1M'));
Current month: 12
Last month: 11
Number of Days in 12th month: 31
Number of Days in 11th month: 30
Dec 31st - 31 days = Nov 31st
Nov 31st = Nov 1 + 31 Days = 1st of Dec (30+1)

Categories