Time Zone Conversion in MySQL
A wise man once said that the safest way to store date and time values in MySQL is to store Unix timestamps in an unsigned INT column. I agree with Baron, but most of us have to deal with DATE, DATETIME, and/or TIMESTAMP values sometimes, and it’s often useful to know how to convert values from one time zone to another.
##The CONVERT_TZ() Function
You can convert a temporal value in MySQL from one time zone to another using the CONVERT_TZ() function. As an example, say I want to watch the Boston Marathon in Melbourne, Australia but I’m afraid it may be on after my bed time. The elite men start at 10:25 AM on April 18, 2016. Boston’s time zone will be EDT that day, which is UTC-4. Melbourne’s time zone that day will be AEST, which is UTC+10. The following query will tell me what time the Boston Marathon starts in Melbourne:
mysql> select convert_tz('2016-04-18 10:25','-04:00','+10:00');
+--------------------------------------------------+
| convert_tz('2016-04-18 10:25','-04:00','+10:00') |
+--------------------------------------------------+
| 2016-04-19 00:25:00 |
+--------------------------------------------------+
1 row in set (0.00 sec)
Therefore the marathon starts are 12:25 AM on Tuesday in Melbourne, which is past my bedtime.
It may seem a little silly to use CONVERT_TZ() with offsets like that rather than just adding 14 hours like this:
mysql> select '2016-04-18 10:25' + interval 14 hour;
+---------------------------------------+
| '2016-04-18 10:25' + interval 14 hour |
+---------------------------------------+
| 2016-04-19 00:25:00 |
+---------------------------------------+
1 row in set (0.00 sec)
My argument for preferring CONVERT_TZ() is that it makes the query much more readable. In the first example it’s clear that I am converting a time from UTC-04:00 to UTC+10:00, whereas in the second example there’s no way to tell from the query why I’m adding 14 hours.
##Populating the Time Zone Tables
Using CONVERT_TZ() becomes more interesting using named time zones, but that only works if the system time zone tables are populated. For example if I try to find out what time the Boston Marathon starts in Melbourne using named time zones I may get a NULL value:
mysql> select convert_tz('2016-04-18 10:25','America/New_York','Australia/Melbourne');
+-------------------------------------------------------------------------+
| convert_tz('2016-04-18 10:25','America/New_York','Australia/Melbourne') |
+-------------------------------------------------------------------------+
| NULL |
+-------------------------------------------------------------------------+
1 row in set (0.00 sec)
Why did I get NULL? Probably because my time zone tables are empty. Those tables are not populated by default.
Following the instructions in the reference manual I can populate those tables by running this command in the shell:
mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root mysql
Now I can use named time zones:
mysql> select convert_tz('2016-04-18 10:25','America/New_York','Australia/Melbourne');
+-------------------------------------------------------------------------+
| convert_tz('2016-04-18 10:25','America/New_York','Australia/Melbourne') |
+-------------------------------------------------------------------------+
| 2016-04-19 00:25:00 |
+-------------------------------------------------------------------------+
1 row in set (0.00 sec)
Bear in mind that time zone rules change sometimes, so you may want to review the manual and develop a plan to stay up to date with those changes.
##Know What Time Zone MySQL is Using
My examples so far have used static datetime values. When dealing with values stored in a table or returned by MySQL functions it’s important to know the context of those values if I want to convert them. For example I need to know what time zone MySQL is using if I want to convert a datetime value stored in a table, or the output of a function like NOW() to a different time zone.
The relevant session and global variables are named time_zone and system_time_zone. You can access them like this:
mysql> select @@session.time_zone,@@global.time_zone,@@global.system_time_zone;
+---------------------+--------------------+---------------------------+
| @@session.time_zone | @@global.time_zone | @@global.system_time_zone |
+---------------------+--------------------+---------------------------+
| SYSTEM | SYSTEM | UTC |
+---------------------+--------------------+---------------------------+
1 row in set (0.00 sec)
If the time_zone variable is set to SYSTEM then the system_time_zone will be used.