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.