Day light saving time and repeating events. What should I store? - php

We have the same system as Google Calendar Events now you can create an event and it will notify you at that time.
PHP cron job will run every 15 minutes, now the settings can be set to send mails every day or weekday or weekend, or every 2nd day of month or weekend. And it can be repeated for few occurrences or forever so we can not store UTC value in the table.I know I can store the UTC for next event and update that column for next event. But I think, there should be a better way to do this then changing UTC on each occurance.
we have a field which was storing offset time "-04:00" or "-05:00" on the basis of timezone you choose.
We had EST, EDT, CST, CDT, PST, PDT, MST, MDT in our drop-down earlier. on the basis of chosen value, we have saved offset in DB which is an incorrect way.
Now when DAYLIGHT SAVING time changes user has to move from EST to EDT or vice versa. Now we want to solve this problem that user does not have to change there settings.
Want to have a drop-down which has "Eastern, Central, Mountain, Pacific" and want to store the value in DB.
What value should be stored ? as we do not want to store offset as it will change with DST and it will be an incorrect way.
Another thing how it works. Our servers are in UTC. we have a custom function in MySQL named "isItCorrectTimeToDoThis" which checks that is there any events are present at this time in UTC in time zones. in this function, we are using the CONVERT_TZ function. We have cron job which runs every 15 minutes but a query to get events is in MySql. I hope you guys have understood this part.
We have Timezone tables installed in our server.
WHat should be stored in db table field timezone "EST", "EDT" or "US/Eastern".
1> I am not able to find what will be the output of CONVERT_TZ(now(),'UTC','EST'); when DST is on.
OR
2> I am not able to find what will be the output of CONVERT_TZ(now(),'UTC','EDT'); when DST is off.
Or
3> CONVERT_TZ(now(),'UTC','US/Eastern')
Which one will handle Daylight savings accurately? That user does not have to change their settings again and again.

Just store the time in UTC and just render the time for the local timezone of the user. This is the best option. The only conversion will be in the UI and that is the only place where you should care for time zones.

Related

Handling timezones and dates

I am having an issue getting my head around storing and retrieving information based on a date (no time) and dealing with multiple timezones.
By default my application uses UTC, so create/update times are all in UTC and PHP functions such as date etc all use UTC.
My problem is this:
I allow users to create a news item, the news item has a date (no time, just date).
The application uses UTC, my user is in Sydney, so what I do is when the form loads I pre-populate the date field based of jS M Y for the Australia/Sydney timezone, this way it shows as today for him. All good so far.
When I save the record into the database (into a DATE field) I use the date they entered so if they said 3rd March 2016, then it goes in as 2016-03-03.
When it gets pulled out again it displays fine for them, but if I do a report saying "How many news items published today?", in my report (cron job) it might be looking for a different date, because if this person entered it in early morning, then 3rd March 2016 for Sydney is still going to be 2nd March 2016 in UTC so there will be 0 for today.
The solution it appears would be to convert the date back to UTC when you store it, but how will that work?.. Since it's just a date with no time.
2nd March 2016 00:00:00 UTC would be 2nd March 2016 10:00:00 Sydney
So.. I am not sure how to solve this without them having to also enter a time, which seems redundant.
Always remember that savings time rules vary between countries, such as the USA and EU. Not just when the clocks change but timings of changes across time zones.
In the past I forced a local hour of 12:00 (noon). With a time value the standard conversion routines be used. Choosing noon kept everything well away from any midnight and savings time issues as an hour slip either way would still be in the same day.
The simplest way is to ALWAYS work with full UTC timestamps under the hood.
Convert incoming dates to these straight away, and only convert back to readable, localized formats when rendering.
This way you have a single, solid, point of reference. Everything in your database has logged exactly when it was created (relative to the 1st of January 1970 - UTC).
Then it becomes trivial to order articles in order of creation (regardless of where their authors posted from). It's also much easier to count how many articles were added in the last 24 hours, or on a specific day (for example; seven days ago) -- notice how these examples don't require a date, just ranges of seconds.
This also makes it easy to show "user A" when "user B" added an article, relative to user A's timezone -- you don't need to look up user B's timezone to do the conversion.
Also, give your user the option to add a time to their articles, but don't make it mandatory. Have a sensible default (ideally a dynamic "now"). This is more natural, and should smooth out the midnight and DST anomalies highlighted by Gilbert, when building reports.
It actually sounds like you are doing things correctly already.
The concepts of "today" and a "date" are calendering concepts, covered by the domain of civil time. You can fly around the world with a printed calendar, and you don't need to adjust it for UTC or time zones. A date itself can't be "in UTC".
However, you can say that in the domain of absolute time, that a date in time zone A does not cover the same instantaneous moments of time that are covered by time zone B. At any given time, there could be two different "todays" in effect on the Earth.
By the perspective of your Sydney user, March 3rd was the publishing date. You might have another user in Hawaii publishing simultaneously at March 2nd.
Inherently then, your posting date is the civil date that the item was posted. When you query for all items posted "today", that is based on the civil date of wherever you're at when you're asking.
If this isn't the desired behavior, then change the question. Instead of asking "what was posted today", ask "what was posted in the last 24 hours?" For that, you would indeed need to store the full UTC-based date and time (not just date) of the post. Then you would query it by UTC time from now less 24 hours.
A thought exercise that will help: What should you do if a user posts at 23:59 UTC, and you run a query one minute later? Should it include the result - or not? Does your answer change if it's 23:59 local time and you run the query one minute later in the same time zone? Thinking this through will help you decide what you should actually be storing and querying.
I'll also say that in most cases you should indeed store a UTC based timestamp, but you may want to also store the local date (or datetime) in a separate field. This gives you the ability to do both types of queries.

Storing specific Date/Time values for users in different time zones

I'm working on a PHP(CodeIgniter)/MySQL application that allows users select when their blog post will be published. How I have designed it to work so far is as below:
User sets the timezone in his/her profile.
User sets the publish date of the blog post. This date/time is assumed to be based on user's timezone.
Based on user's timezone, the publish date is converted to UTC (00:00).
The converted date is stored as DATETIME in mysql.
Server frequently converts the stored DATETIME values to server timezone and publishes the content when their time has come.
I was wondering if there is a better way to handle this situation, considering that my DB server and web server are on different machines on the cloud and might change location as well.
Since you are talking about scheduling of future time, storing only UTC isn't necessarily the best approach.
Keep in mind that time zone rules can (and do) change. If you apply an update to your time zone data (in PHP, it's done with PECL's timezonedb package), then any data you already converted to UTC might be invalid.
The better solution is to store multiple values:
The original local date and time
The original time zone (ex. "America/New_York")
The UTC date and time, converted from the local values
When you want to see if it's time to run the task, you'd compare the current UTC date and time against the stored UTC value.
When you apply time zone updates, you throw out the previous converted UTC values for all future entries, and recalculate new ones based on the updated data.
If you don't do this, then you're losing the intent that the user originally provided. And if you don't keep your time zone data updated, then your system won't be aware of various changes happening around the world, such as the changes made in Egypt earlier this year, or the changes coming in October for Russia.
Last point - If implemented properly, the time zone of your server should not matter. Compare UTC to UTC.

How to store repeating dates keeping in mind Daylight Savings Time

I'm storing events in my database. I have 'start' and 'end' date times, 'tickets_start' and 'tickets_end' (for when ticket sales actually start/end - as opposed to the start/end of the actual event).
So far, I've built methods that do all the fun stuff like converting the date/times to GMT before saving, then back to their respective timezone for display.
I'm storing the timezone in a varchar field with values like "America/New_York".
But - now I need to start dealing with if the user wants to allow repeating events. I've done it before, and it's not that big a deal, but never across multiple timezones.
At first, I thought it'd be no big deal, but then realized that - if the initial start date was in July (as example), and it repeats every month for a year, at some point, Daylight Savings Time will make it so that the conversion from GMT will change a time differently. One month, when converting 12:00, it would change it to -5, and the next, it would change it to -4 because of DST.
My current thought is that I'll store a 'dst' tinyint(1) for whether the start/end dates were entered during DST, and then make a method to alter the time by an hour if/when necessary.
But - figured I'd ask here in hopes maybe there's a "normal" for this or an easy something that I'm not thinking of.
(cakephp 2.4.x)
First, please recognize that in modern terminology you should say UTC instead of GMT. They are mostly equivalent, except that UTC is more precisely defined. Reserve the term GMT to refer to the portion of the time zone in the United Kingdom that is in effect during the winter months having the offset UTC+0.
Now to your question. UTC is not necessarily the best way to store all date and time values. It works particularly well for past events, or for future absolute events, but it doesn't work so great for future local events - especially future recurring events.
I wrote about this on another answer recently, and is one of the few exception cases where local time makes more sense than UTC. The main argument is the "alarm clock problem". If you set your alarm clock by UTC, you'll be waking up an hour early or late on the day of the DST transitions. This is why most folks set their alarm clocks by local time.
Of course, you can't just store a local time if you are working with data from all over the world. You should store a few different things:
The local time of the recurring event, such as "08:00"
The time zone that the local time is expressed in, such as "America/New_York"
The recurrence pattern, in whatever format makes sense for your application, such as daily, bi-weekly, or the third Thursday of the month, etc.
The next immediate UTC date and time equivalent, to the best that you can project it.
Perhaps, but not always, a list of future event UTC date and times, projected out some predefined period into the future (perhaps a week, perhaps 6 months, perhaps a year or two, depending on your needs).
For the last two, understand that the UTC equivalent of any local date/time can change if the government responsible for that time zone decides to change anything. Since there are multiple time zone database updates every year, you will want to have a plan to subscribe to announcements of updates and update your timezone database regularly. Whenever you update your timezone data, you'll need to recalculate the UTC equivalent times of all future events.
Having the UTC equivalents is important if you plan to show any kind of list of events spanning more than one time zone. Those are the values you'll query to build that list.
Another point to consider is that if an event is scheduled for a local time that occurs during a DST fall-back transition, you will have to decide whether the event occurs on the first instance (usually), or the second instance (sometimes), or both (rarely), and build into your application a mechanism to ensure the event doesn't fire twice unless you want it to.
If you were looking for a simple answer - sorry, but there isn't one. Scheduling future events across time zones is a complicated task.
Alternative Approach
I've had a few people show me a technique where they do use UTC time for scheduling, which is that they pick a starting date in local time, convert that to UTC to be stored, and store the time zone ID as well. Then at runtime, they apply the time zone to convert the original UTC time back to local time, then use that local time to compute the other recurrences, as if it were the one originally stored as above.
While this technique will work, the drawbacks are:
If there's a time zone update that changes the local time before the first instance is run, it will throw the whole schedule off. This can be mitigated by choosing a time in the past for the "first" instance, such that the second instance is really the first one.
If the time is really a "floating time" that should follow the user around (such as in an alarm clock on a cell phone), you'd still have to store time zone information for the zone it was originally created in - even if that's not the zone you want to run in.
It adds additional complexity without any benefit. I'd reserve this technique for situations where you may have had a UTC-only scheduler that you're trying to retrofit time zone support into.

How to calculate time zone offset for a large application?

As working on a large application I am trying to make the datetime stamps reconcile with the current user time. so If activity id done at 3:00PM then the every user see it at 3:00PM
So Here is solution steps to the problem. on this and please correct me or lead me to the right direction if I am not on the right direction.
Store all datetime in MySql as UTC time.
Store time zones in a table with the current offset. for example
zone_id zone_name utc-offset
1 Central -6
In my users table I have a field for user_time_zone_id and in that field I will have the value "1" in the user setting so it will say that this user is using the system from "Central" location.
In my php application configuration I set the default time zone to UTC like this
date_default_timezone_set('UTC');
Once I load this application I define the user offset and on each datetime out put I do the calculation of the time. for example date('Y-m-d', strtotime($current_offset.' hour')) where $current_offset = -6 as it is define by the user profile upon the page load.
Here are my questions.
Is my approach to this problem correct?
Is there a better way of doing this?
How to calculate the daylight saving time? Please keep in mind that there are some parts of the country that does not have daylight saving.
I had a similar thing one time, and it ended up being a pain to try to keep track of users timezones and daylight savings, especially when half your clients are in AZ which doesn't have daylight savings. I don't know if this is possible for you, but what I ended up doing was just store everything in UTC and then used JS to convert it to the users local time with the Date object. It was done through an ajax call, but you could also echo a document.write if you needed to.
You shouldn't need to use date_default_timezone_set, use the PHP DateTime class which has native support for timezones. Or, like #romo says, you can do it on the client in JavaScript.

PHP, Codeigniter: How to Set Date/Time based on users timezone/location globally in a web app?

I have just realised if I add a particular record to my MySQL database - it will have a date/time of the server and not the particular user and where they are located which means my search function by date is useless! As they will not be able to search by when they have added it in their timezone rather when it was added in the servers timezone.
Is there a way in Codeigniter to globally set time and date specific to a users location (maybe using their IP) and every time I call date() or time() that users timezone is used.
What I am actually asking for is
probably how to make my application
dependent on each users timezone?
Maybe its better to store each users timezone in their profile and have a standard time (servers time) and then convert the time to for each user?
Thanks all
It sounds like what you need to do is store all of the date and times in your system as UTC time (used to be called GMT). This is the base time that everything in the world is calculated off of with adjustments. (eg: Central Time is -6 hours off of UTC)
In MySQL you can use UTC_TIMESTAMP() to get the current UTC time as long as your server and DB are configured with the correct times and timezone settings.
In PHP run this to set the timestamp of PHP to UTC (you will run this in your code so put it on every page or in a centralized index file):
date_default_timezone_set('UTC');
Or you can go directly into PHP.INI and tell it to use UTC time globally. (this may not work if you have multiple websites on a single installation of PHP.
And then anywhere in the system you need to get the current UTC time you can just call:
time();
Then for each user in the system you will need to ask them what timezone they live in and then when you display times make the adjustment for that user. So if it is 5:00PM UTC and I live in Easter US (-5) the time would be 5:00 - 5 hours = 12:00PM.
This can be a long process to get right but once you do your users will find it very useful especially internationally.
I think the easiest way is define a timezone for internal data storage (your server's time zone or UTC) and convert the time according to the user's time zone when outputting it.
I don't know CodeIgniter so I can't point you to the right functions. One prominent library that is timezone aware is Zend_Date: Working with Timezones I have not worked with these functions yet but they look promising.
However, once you know the user's time zone, it's not difficult to put together an own function that adds/substracts the offset when outputting dates and/or times.
Possibly related question:
MySQL: keep server timezone or user timezone?
Take an example of an existing web application such as WordPress and phpBB. Each user have their own timezone setting.
When receiving a content from the user, use local_to_gmt() function in the Date Helper then save the content into database using the gmt date. When fetching the data you will get the time in gmt. Get the user's timezone setting, then display the data in that timezone.
This way, you can save yourself from calculating between two timezone. Just make sure that your server's time is in correct setting, so all your data is in the correct gmt time.
UPDATE:
Recently I review the last project I worked on that have timezone issue. After thinking various scenario, here is the solution for the timezone issue:
All data stored right now already
using server's time. Changing this
will takes times and prone to error,
so I leave it like that.
For the new data from user that set the content date to a certain
date and time, I stored it into 2
column. First column is to store the
data as is, and used to displaying
it as is. Second column will be a
recalculation of the date based on
the user's timezone into server's
timezone. This column is used in the
WHERE statement (filter based on
server date) and for the ORDER
(because this column's value all in
same timezone, which is the server's
timezone).
This way, I only do 1 timezone calculation, which is to convert user date into server date. For displaying , I display the date according to server's datetime. Since all data stored in the same timezone, data can be ordered by the column that hold the server date value.
For the user that have set their timezone, the date from database can be easily recalculated to get the datetime in the user's timezone. Btw, in my application, I display the date using timeago jquery plugins. This plugins need time in the ISO8601 format (UTC time). local_to_gmt() function in CodeIgniter can be used to do this.
Obviously the leap to British Summer Time (Daylight Savings Time) is a big confusion in the world of programming, and I am indeed caught up in that confusion.
The best possible solution I can find (which I will try to coherently explain) when using a timezone sensitive system is this:
The Web Server and Database should both be running off the same machine timezone. I suggest UTC as it is the building blocks of timezone conversions. This will ensure that all of the dates stored in your database are constant, and don't skip any times such as the 1hour jump between Daylight Savings.
At the top of all of your PHP scripts use date_default_timezone_set('Europe/London'); with the specific timezone of the user.
When producing dates from user submitted forms, use gmmktime(); to ensure that the timestamp created is UTC and not altered by the timezone that you have set.
date(); can be used when displaying dates, as this will convert the timestamp to the correct time taking into account the timezone that you have set.
If you do need to show a date in the UTC format then use gmdate(); with the $gm_timestamp that you have taken from the database or created with gmmktime();.
I have written this bit of PHP to help understand the situation.
date_default_timezone_set('UTC');
$gmtime = gmmktime(2,0,0,03,29,2009);
$time = mktime(2,0,0,03,29,2009);
echo $gmtime.'<br />'.date('r',$gmtime).'<br />'.gmdate('r',$gmtime).'<br />';
echo $time.'<br />'.date('r',$time).'<br />'.gmdate('r',$time).'<br />';
date_default_timezone_set('Europe/London');
$gmtime = gmmktime(2,0,0,03,29,2009);
$time = mktime(2,0,0,03,29,2009);
echo $gmtime.'<br />'.date('r',$gmtime).'<br />'.gmdate('r',$gmtime).'<br />';
echo $time.'<br />'.date('r',$time).'<br />'.gmdate('r',$time).'<br />';
Hopefully I've done a good job, but I doubt it because I'm still trying to battle the problem in my head.
UPDATE:
Glad I did this, because I am now having doubts about the user inputted dates.
To get the User inputted date (taking into account their timezone) to match up with the UTC corresponding date in the database, you should put it through mktime(). And then use gmdate('U', $timestamp); to get the true UTC timestamp. (I think)
Example
Looking at it from a reporting side, the user is using the 'Europe/London' timezone. At the start of our PHP script we call date_default_timezone_set('Europe/London');, whilst the Database (and all the records within) is still in UTC.
The user then sends through that they want to select a list of books added to the database between 25/03/2010 10:00 to 30/03/2010 14:00. The PHP script then runs the date variables through mktime($hour, $minute, $second, $month, $day, $year) to generate a correct UTC timestamp. The start date will not change, but PHP knows that the end date is within the BST timezone, so changes the timestamp to UTC accordingly.
When the results are returned back to the user, date('r', $date_added) can be used to show the user the date the book was added to the database according to their set timezone.
This link may help with understanding when it changes. http://www.daylightsavingtime.co.uk/
I think recalculating to user's time is better option, since it gives you normalized time on server, i.e. if you'll need to look up something, that happened (from your point of view) hour ago, you won't have a mess with american, asian and e.g. australian time.
Just ask them for their timezone (usually select with major cities in that timezone) and then recalculate :)
Or, alternatively, you can store two timedates - one for your comparison and one to show, so you won't have so much calculations on serverside.
Also, if recalculating, you can use date helper:
http://ellislab.com/codeigniter/user-guide/helpers/date_helper.html
I've used the MySQL built-in timezone conversion. In the database, all datetimes are stored as UTC. In the select query, I used CONVERT_TZ to convert to the user's timezone. You can specify timezone codes or hour invervals like:
SELECT CONVERT_TZ('2004-01-01 12:00:00','GMT','MET');
SELECT CONVERT_TZ('2004-01-01 12:00:00','+00:00','+10:00');
But, the problem is this does not accommodate for daylight savings times. This is particularly frustrating since many parts of the world either don't honor daylight savings or honor it on different dates. So, if you install the timezone description tables, you can use descriptive names that will account for daylight savings automatically like:
SELECT CONVERT_TZ('2004-01-01 12:00:00', 'UTC', 'US/Eastern');
Codeigniter contains a helper which deals with all manner of date functions.
Codeigniter Date helper
the command gmt_to_local() should help you... the third parameter is for 'daylight_saving'.
Takes a Unix timestamp (referenced to GMT) as input, and converts it to a localized timestamp based on the timezone and Daylight Saving time submitted. Example:
$timestamp = '1140153693';
$timezone = 'UM8';
$daylight_saving = TRUE;
echo gmt_to_local($timestamp, $timezone, $daylight_saving);
Add this line to autoload.php in the application folder/config folder:
$autoload['time_zone'] = date_default_timezone_set('Asia/Kolkata');

Categories