MySQL 将时间戳转换为搜索查询的时区

MySQL convert timestamp to timezone for search query

我有一个 MySQL 数据库,其中包含一个名为 orders 的 table 和一个名为 created_at

timestamp

所有时间戳都以 UTC 格式存储,并在前端转换回用户本地时间 - 目前设置为 'Europe/London'

在伦敦,我们在 4 月和 11 月之间实行夏令时,时钟为 GMT+1,例如在 27-10-2019 00:00 本地时间创建的订单将保存为 26-10-2019 23:00 UTC

用户可以搜索在特定日期之间创建的所有订单。在 MySQL 中,我可以使用以下函数将日期转换回本地时区以实现此目的,例如:

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'

但是我在共享主机上并且 MySQL 时区 table 未加载,因此无法使用命名时区。我只需要使用一个时区,您可能会想为什么不将所有时间戳存储为 Europe/London 而不是 UTC。我想将其保留为 'UTC' 以适应将来的其他时区。

是否有解决方法 return 在不使用指定时区的情况下获得正确的结果?

注意查询实际上是使用 php 中的 laravels eloqeunt 查询生成器编写的,如下所示:

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')]);
}

起初我认为一个解决方案是将用户输入的日期也转换为 UTC,但是当我在以下日期创建订单时,结果遗漏了第二条记录:

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)

感谢任何帮助。

* 更新 *

通过递增结束日期(当夏令时未生效时,使用 < 而不是 <= 检查的结果日期时间不起作用。在英国,今年夏令时已关闭2019-10-27。假设您在以下日期创建了订单:

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)

用户想要列出在 2019-10-27 上创建的所有订单。使用 2019-10-27 作为用户选择的日期,开始日期变为 2019-10-26 使用 php Carbon:

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

结束日期变为 2019-10-28

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

这意味着查询最终为:

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

因此,在上述情况下,您最终得到的结果集将包含在 2019-10-26 上创建的订单,这不是您想要的结果。

您应该向您的托管服务提供商投诉。如果时区表未安装并保持最新,则实际上是 MySQL.

的安装损坏

但即使它们存在,您也应该在查询中将请求的日期范围转换为 utc,而不是相反:

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')

这样效率更高,并且允许使用 created_at 上的索引。

如果没有时区表,您可以对 php 中的开始日期和结束日期执行转换,这与上面的 SQL 重复。

请注意,您正在转换为日期时间,而不是日期,并且您的结束日期需要递增(并且生成的日期时间使用 < 而不是 <= 检查)以正确包含完整的日;我怀疑这可能是您在代码中尝试转换为 utc 时出现问题的根源。