Tuesday 19 November 2013

Avoiding timezone nightmares in PHP and MySQL

Avoiding timezone nightmares in PHP and MySQL »

FERDY CHRISTANT - APR 19, 2010 (07:08:48 PM)

I am a developer that finds things like date handling, character encodings and such to be a pain. Like many developers, I pretend the problem does not exist and just hope that things turn out well. This is an attitude that I can no longer afford in this global village, which is why I'm getting to the bottom of things like this. An example is one of my earlier articles, where I had to take a deep dive into Unicode:
Although time zones and daylight savings time are entirely unrelated, they are similar, undesirable problems that you cannot hide from. In this little article I will tell you how I deal with timezones and DST in a PHP/MySQL context. The key of this article is in avoiding timezone issues in your date handling.
Introduction
There are three aspects to date/time handling where timezones can be relevant:
  • Storing date/times
  • Calculating with date/times
  • Displaying date/times
Storing date times
The key to storing date times is to explicitly set the timezone or to set it to a neutral timezone. You should never just take any date and insert it into a database without explicitly converting it to a specific timezone. In most cases, you are best of storing it in a neutral format, meaning the GMT timezone. The GMT timezone is also used by the UTC standard. This little PHP snippet converts the current date/time into GMT:
 $timestamp gmdate("Y-m-d H:i:s"time()); 
Next, one can insert this into MySQL without change if your table column is of type "timestamp". Note that in the example above, time() will give back the time using the timezone configured at the host. In my case it is on GMT+2. This means that if you actually look in the MySQL table, all dates will be 2 hours off. This is correct behavior, since we are storing the timestamp in the GMT timezone. The great thing here is that it does not matter in which timezone your host is.
(note: It is recommended though to set both your web host and MySQL to the same timezone explicitly)
Calculating with date times
Once we have stored timestamps in a neutral timezone, doing calculations on them is fairly easy. The thing to remember here is that if you are doing timestamp comparisons, your input timestamp first has to be converted to GMT too. This is best illustrated with an example:
SELECT COUNT(id) FROM c WHERE date_created > DATE_SUB(UTC_TIMESTAMP(), INTERVAL 1 HOUR)
The line of SQL above returns the amount of comments that were made in the last hour. The date_created column contains the timezone neutral timestamp. Since we are calculating a date offset, our input parameter (the current timestamp) first has to be converted to GMT/UTC. That's all we need to do, because date offsets are not time zone dependent in any other way. What happens one hour ago in one part of the world happened one hour ago in any other part of the world.
Displaying date times
When it comes to displaying date/times that are sensitive to a user's timezone, it takes a conversion from the neutral timestamp to a timestamp that is optimized to the user's timezone. You can do this straight from MySQL using theConvert_TZ function.
That's only part of the story though. You will need a reliable way to detect the current user's timezone or let them set it theirselves. Next, you need a way to convert the user-friendly list of timezones to the one used in MySQL or PHP. Finally, you can then display a date/time for the user's timezone.
Luckily, in some projects you may be able to escape this mess. If your customer accepts a date format like this:
"3 days ago"
Instead of
"April 16, 2010"
...then the whole problem of displaying timezone specific dates goes away. In that case, you can use the technique used in this article.
Closing
This mini article was just me thinking out loud and structuring my thoughts around this topic. Still, I hope it helps someone else. Please do rate and comment below

Sunday 17 November 2013

timezone issues

Summary of answers and other data: (please add yours)

Do:
  • Whenever you are referring to a particular moment in time, persist the time according to a unified standard that is not affected by daylight savings. GMT and UTC have been mentioned by different people, though UTC seems to be mentioned most often.
  • ?? Include the local time offset as is (including DST offset) when storing timestamps. ?? Store timestamps as UTC or epochs(number of seconds since 1970) without time zone or offset.
  • Include the original time zone name, so you can reconstruct the original time at a later point and display correct offsets if needed.
  • Remember that DST offsets are not always an integer number of hours (for example, Indian Standard Time is UTC+05:30).
  • If using Java, use Joda Time.
  • If using .NETconsider using Noda Time.
  • If using Perl, use DateTime.
  • If using Python, use pytz
  • If using PHP > 5.2, use the native time zones conversions provided by DateTime, and DateTimeZone classes. To keep PHP with up to date Olson data install periodically this PECL package. See answer
  • Create a table TZOffsets with three columns: RegionClassId, StartDateTime, and OffsetMinutes (int, in minutes). See answer
  • Business rules should always work on civil time (UTC/GMT).
  • Internally, keep timestamps in something like civil-time-seconds-from-epoch. See answer.
  • Only convert to local times at the last possible moment.
  • Remember that time zones and offsets are not fixed and may change. For instance, historically US and UK used the same dates to 'spring forward' and 'fall back'. However, in the mid 2000s the US changed the dates that the clocks get changed on. This now means that for 48 weeks of the year the difference between London time and New York time is 5 hours and for 4 weeks (3 in the spring, 1 in the autumn) it is 4 hours. Be aware of items like this in any calculations that involve multiple zones.
  • Consider the type of time (actual event time, broadcast time, relative time, historical time, recurring time) what elements (timestamp, time zone offset and time zone name) you need to store for correct retrieval - see "Types of Time" in answer.
  • Check if your DBMS needs to be shutdown during transition.
  • Keep your OS, database and application tzdata files in sync, between themselves and the rest of the world.
  • On Servers, set hardware clocks and OS clocks to UTC.
  • Use NTP services on all servers.
  • If doing historical auditing store both UTC and local time (this allows exact pinpointing of time, as conversion tables will change).
  • If using FAT32, remember that timestamps are stored in local time, not UTC.
  • When dealing with recurring events (weekly TV show, for example), remember that the time changes with DST and will be different across time zones.
Don't:
  • Do not use JavaScript date and time calculations in web applications unless you ABSOLUTELY have to.
  • Never trust client datetime. It may very well be incorrect.
  • Do not compare client datetimes with server datetimes.
Testing:
  • When testing make sure you test countries in the Western and Eastern hemispheres, with both DST in progress and not and a country that does not use DST (6 in total).
  • Test all third-party libraries and applications and make sure they handle time zone data correctly.
Reference:
Other:
  • Lobby your representative to end the abomination that is DST. We can always hope...
  • Lobby for EST

Angular Tutorial (Update to Angular 7)

As Angular 7 has just been released a few days ago. This tutorial is updated to show you how to create an Angular 7 project and the new fe...