如果creation_date_time 列是 TIMESTAMP,则该值在内部存储通用协调时间 (UTC),但是当检索到它时,它将转换为当前会话时区,这将是服务器的默认时区,除非您已将会话时区设置为不同的。当您将此值 $row0['creation_date_time'] 分配给 PHP DateTime 而不明确设置时区时,它将使用 UTC。
为了保持一致并假设您要使用特定的时区,您应该为您的 MySql 连接显式设置会话时区。所以纽约的人可能会做以下事情:
set session time_zone = 'America/New_York'; /* MySql */
在 PHP 中:
$date = new DateTime($row0['creation_date_time'], new DateTimeZone('America/New_York'));
或者,如果您不想使用UTC 以外的任何内容创建DateTime,那么您至少应该在检索 TIMESTAMP 之前将您的 MySql 会话时区设置为 UTC。然后,您将拥有一致的时区,但不一定是您想要的可显示值。
例如,如果在 2020 年 7 月 1 日凌晨 5 点在纽约发生了一个事件,并且该事件的时间戳保存在 TIMESTAMP 列中,则在内部它将存储为 '2020-07-01 01:00:00 ' 因为有 4 小时的差异。 假设使用NOW() 或CURRENT_TIMESTAMP() 的值来存储时间戳,这将独立于事件发生时有效的当前会话时区(但是,如果您存储了文字到 TIMESTAMP 列中,它将在转换为 UTC 之前在当前会话的时区中进行解释)。但是,当我检索该值时,我希望它反映“本地”时间。因此,如果我希望检索到的值为“2020-07-01 05:00:00”,我需要在获取列之前将会话时区设置为 America/New_York。如果我将其分配给 PHP DateTime 而不设置适当的时区,它将显示为上午 5 点,但使用 UTC 时区,这并不正确。
我不确定以上所有内容是否真的回答了您的问题,所以让我告诉您在没有知识或您的实际情况的情况下我通常会采取的方法:
我不会尝试使用 PHP 初始化变量显式更新时间戳,而是允许 MySql 根据您的要求在创建和/或更新时进行初始化:
UPDATE my_table set creation_date_time = now(), etc.
问题在于,当您使用文字值设置 TIMESTAMP 列时,它将在当前会话时区的任何位置进行解释,并且如果不明确设置它,这将变得不确定,除非您可以控制系统时区(应用程序也迁移到不同的 ISP 和时区,至少在我的国家是这样)。即使您将按照我的建议明确设置会话时区,但让 MySql 进行初始化要简单得多。
然后我会确保您在连接后做的第一件事就是设置本地会话时区:
set session_time_zone = 'Europe/Berlin';
然后你的 PHP 代码是:
$date1 = new DateTime($row0['creation_date_time'], new DateTimeZone('Europe/Berlin'));
$date1 = $date1->modify("+1 month");
$date2 = new DateTime("now", new DateTimeZone('Europe/Berlin'));
if ($date1 <= date2) {
// expired
}
更新
如果当前 MySql 服务器时间是“Europe/Berlin”,那么当您执行 SELECT TIMEDIFF(NOW(), UTC_TIMESTAMP) 查询时,我希望您得到的结果是 '02:00' 而不是 '05:30',正如所证明的那样通过以下方式:
mysql> set session time_zone = 'Europe/Berlin';
Query OK, 0 rows affected (0.19 sec)
mysql> SELECT TIMEDIFF(NOW(), UTC_TIMESTAMP);
+--------------------------------+
| TIMEDIFF(NOW(), UTC_TIMESTAMP) |
+--------------------------------+
| 02:00:00 |
+--------------------------------+
1 row in set (0.00 sec)
因此,无论 MySql 服务器时间是什么,它似乎都是柏林以东 03:30 小时。现在您要比较的时间戳是:
来自 MySql:
DateTime Object ( [date] => 2020-07-25 15:31:46.000000 [timezone_type] => 3 [timezone] => Europe/Berlin )
来自 PHP:
DateTime Object ( [date] => 2020-07-25 12:40:33.451120 [timezone_type] => 3 [timezone] => Europe/Berlin )
现在要精确比较这两者有点困难。如果您与 MySql 时间戳同时创建 PHP 时间戳,并且 MySql 时间戳是使用诸如 NOW() 之类的 MySql 函数创建的(在这种情况下,TIMESTAMP 将在内部正确),那么我希望 MySql 时间戳为 03:30晚于 PHP 时间戳(因为从 UTC 转换回 TIMESTAMP 时时区有效)。但是,如果您实际上在大约 40 分钟后创建了 PHP 时间戳,那么您所显示的或多或少就是我所期望看到的。 你告诉我 40 分钟是否更接近两个事件之间的经过时间。假设是,那么这也告诉我你一直在用 NOW() 或等效的 @ 初始化 TIMESTAMP 列987654343@,这将是一件非常好的事情,因为在内部时间戳将是正确的,而您唯一的问题是当您检索它时,您会将其转换为错误的时区。但这可以通过在连接后立即设置会话时区来解决:
set session time_zone = 'Europe/Berlin'
更新 2
如果您无法设置上述时区,则说明您的 MySql 安装中缺少时区,您需要加载它们(您在 PHP 大小上看到时区的原因是因为 PHP 具有时区,并且您将其显式加载到您的DateTime 对象)。
URL https://dev.mysql.com/downloads/timezones.html 包含时区文件。选择适合您的 MySql 版本的 POSIX 版本并将其下载到您的 PC。
URL https://dev.mysql.com/doc/refman/8.0/en/time-zone-support.html 有加载此文件的说明。阅读本文并确保您了解自己在做什么(必须有人承担责任)。简而言之,您将执行:
mysql_tzinfo_to_sql path-to-downloaded-timezones-file | mysql -u admin-user-id -p admin-password
看完之后,如果你还想轻点阅读,请看What difference between the DATE, TIME, DATETIME, and TIMESTAMP Types,特别是我给出的答案(Booboo)。