简体   繁体   中英

MySQL convert timestamp to timezone for search query

I have a MySQL database with a table called orders and a timestamp column called created_at

All timestamps are stored in UTC format and converted back to the users local time on the front end - currently this is set to 'Europe/London'

In London we have DST between April and November where the clocks are GMT+1 eg an order created at 27-10-2019 00:00 localtime would be saved as 26-10-2019 23:00 UTC

A user can search for all orders created between certain dates. In MySQL I can use the following function to convert the dates back to local timezone to achieve this eg:

SELECT * 
  FROM orders 
 WHERE DATE(CONVERT_TZ(created_at, 'UTC','Europe/London')) >= '27-10-2019' 
   AND DATE(CONVERT_TZ(created_at, 'UTC','Europe/London')) <= '27-10-2019'

However I am on a shared hosting and the MySQL timezone tables are not loaded so unable to use named timezone. I only need to use the one timezone and you probably thinking then why not just store all timestamps as Europe/London instead of UTC . I want to keep it as 'UTC' to accommodate other timezones in the future.

Is there a workaround to return the correct results without using named timezone?

Note the query is actually written using laravels eloqeunt query builder in php as follows:

if (isset($queryString['from_date'])) {
    $query = $query->whereRaw('DATE(orders.created_at) >= ?', [Carbon::parse($queryString['from_date'])->format('Y-m-d')]);
}

if (isset($queryString['to_date'])) {
    $query = $query->whereRaw('DATE(orders.created_at) <= ?', [Carbon::parse($queryString['to_date'])->format('Y-m-d')]);
}

At first I thought a solution would be to convert the user entered dates to UTC as well however when I have orders created on the following dates, the results miss out the second record:

created_at
-------------------------------------------------------------
27-10-2019 00:00 (localtime) - saved as 26-10-2019 23:00 (UTC)
27-10-2019 01:00 (localtime) - saved as 27-10-2019 00:00 (UTC)

Any help appreciated.

* UPDATE *

By incrementing the end date (and the resulting datetime checked with < instead of <= does not work when DST is not in effect. In the UK DST was switched off this year on the 2019-10-27. Lets say you have orders created on the following dates:

created_at (Europe/London)                   created_at (UTC) as saved in DB
------------------------------------         --------------------------------
2019-10-26 23:00                             2019-10-26 22:00  (UTC)
2019-10-27 03:00                             2019-10-27 03:00  (UTC)

User want to list all orders created on the 2019-10-27 . Using 2019-10-27 as the user selected date, the start date becomes 2019-10-26 using php Carbon:

Carbon\Carbon::parse('27 Oct 2019', 'Europe/London')->timezone('UTC')->format('Y-m-d')
=> "2019-10-26"

and end date becomes 2019-10-28

Carbon\Carbon::parse('27 Oct 2019', 'Europe/London')->addDay()->timezone('UTC')->format('Y-m-d')
=> "2019-10-28"

Which means the query ends up as:

WHERE created_at >= "2019-10-26"
  AND created_at < "2019-10-28"

So in the above scenario you end up with a result set that will include orders created on the 2019-10-26 which was not the desired results.

You should complain to your hosting provider. If the timezones tables are not installed and kept up to date, it is in effect a broken installation of MySQL.

But even if they were there, you should convert the requested date range to utc in your query, not the other way around:

WHERE created_at >= CONVERT_TZ('2019-10-27', 'Europe/London', 'UTC')
AND created_at < CONVERT_TZ(DATE_ADD('2019-10-27', INTERVAL 1 DAY), 'Europe/London', 'UTC')

This is more efficient, as well as allowing an index on created_at to be used.

Without the timezones tables, you can perform the conversion on the start and end dates in php that duplicates the SQL above.

Note that you are converting to a datetime, not a date, and your end date needs to be incremented (and the resulting datetime checked with < not <= ) to properly include the full day; I suspect this may have been the source of your problem when trying to convert to utc in your code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM