如何在脚本中处理 MySQL 时区

How to handle MySQL timezone in script

我正在开发移动应用程序。从应用程序调用 Web 服务,该服务根据模式 (?mode=xx)

运行不同的查询

在其中一些查询中,我使用日期函数,例如 DATE(NOW())。

MySQL 数据库中存储的数据以 GMT-7(加拿大山地时间)存储。

我还没有为此 Web 服务注册 domain/host,但当我注册时,可以说它托管在不同的城市,例如多伦多(GMT-5 - 提前 2 小时)。然后在 10:05pm 加拿大山区时间,用户使用该应用程序发送 Web 请求调用,其中包含如下查询:

SELECT DATE(NOW()) 

因为服务器托管在多伦多,所以这将是 return 明天的日期,即使用户所在的位置是前一天并且应用程序显示基于当天的数据。

有人对此有什么想法吗?

编辑:

SYSTEM
2015-01-29 16:19:48
2015-01-29 23:19:48

是运行查询的结果select@@time_zone,now(),utc_timestamp()

查询处理日期 (yyyy-mm-dd) 和时间 (hh:mm:ss) 列类型。

你 运行 在你的 MySQL 服务器上进行这个时间诊断查询。

select @@time_zone, now(), utc_timestamp()

从您的本地时间和 utc 时间可以看出,您的服务器计算机的系统时区设置是 'Canada/Mountain',而 MySQL 服务器软件没有自己的时区设置。

如果你拿起你的桌子并将它们原封不动地移动到附近某个时区的服务器,你可以随时更新你的软件以发出命令

set time_zone = 'Canada/Mountain';

从您的软件连接后。这将使您的新 MySQL 连接表现得像您当前的时区连接一样。如果您拥有 MySQL 服务器,您可以根据此页面上的说明设置其默认时区。 http://dev.mysql.com/doc/refman/5.5/en/time-zone-support.html

现在,这是关于时间数据类型的故事。 DATETIMEDATETIME 都是 timezone-igno运行t。一旦您存储了一个 date/time 值,即使您更改了时区设置,您也会得到相同的值。

TIMESTAMP 数据类型 时区敏感。这些数据项始终存储在 UTC, also known as Z, time,以前称为格林威治标准时间。它们在存储时始终转换为 UTC,并在检索时始终转换回来。

用于获取当前日期和时间(NOW() 和朋友)的 builtin functions 时区敏感。他们将在当地时间产生价值。例外是以 UTC_ 开头的三个函数,它们以 UTC 时间产生值。

许多 MySQL 多时区应用程序使用以下操作规则:

  1. 向每个用户询问用户偏好的时区,或从有关用户的其他一些个人数据中找出。 (电话已从网络向其提供此信息。)代表用户将其存储为 zoneinfo-friendly 时区描述符('America/New_York'、'Canada/Mountain'、'Europe/Vienna' 等) .
  2. 代表用户建立 MySQL 会话后,使用 set time_zone 查询设置用户的时区,如上所示。您应该在 connect 操作后立即执行此操作。
  3. 将用户的日期和时间存储为 TIMESTAMP 数据类型。它们在存储时将转换为 UTC。
  4. 根据需要检索它们。他们将转换回当地时间。

这个想法是您的用户的时区是她上下文的一部分。这很有效,因为如果用户 A 在温哥华而用户 B 在哈利法克斯,并且由于某种原因用户 B 查看用户 A 的时间数据,它将或多或少自动以大西洋时间显示给 B。

它也很好,因为它可以清晰地处理全球变幻莫测的夏令时到标准时间的变化。去年夏天的时间戳将显示为去年夏天的当地时间。

许多全球使用的服务器管理员将他们的系统服务器时间或他们的 MySQL 默认时区设置为 UTC。 (你的没有。)

处理这一切的另一种方法是您开始的方式。选择一个时区并存储与该时区相关的时间戳。在这种情况下,最好选择一个不会在夏令时和标准时间之间交替的时区。然后,在将时间存储到数据库中时,显式转换。您可以通过这样做来存储渥太华用户的时间。

INSERT INTO tbl (appt) VALUES ( 'whatever-time' - INTERVAL 120 MINUTE)

并且您将以相同的方式获取值。这很容易出错,但你可以让它工作。

最后,您可以自己进行转换。 如果您想知道某个任意时区和 UTC 之间有多少分钟的偏移量,请尝试这两个查询。

set time_zone = 'Canada/Atlantic';
select timestampdiff(minute, utc_timestamp(), now());

每年的这个时候返回 -240,也就是 -4:00。您需要使用分钟而不是小时,因为在某些国家/地区存在半小时或一刻钟的时区偏移。

最后,小心。 TIMESTAMP 数据类型不代表 1970 年之前的时间。而且,在我的 MariaDB 10.0 实例上,当时间从32 位。