【问题标题】:Different time in mysql database timestamp and new DateTime() functionmysql数据库时间戳和新DateTime()函数中的不同时间
【发布时间】:2020-07-25 10:53:49
【问题描述】:

当用户执行某些操作然后在数据库中插入一条记录时,我正在使用 xampp

$creation_datetime = new DateTime($row0['creation_date_time']);

上述语句的输出,其中 creation_date_time 是数据库中的时间戳类型,并在插入记录时自动填充。

DateTime Object ( [date] => 2020-07-25 15:31:46.000000 [timezone_type] => 3 [timezone] => Europe/Berlin )

我看到数据库中显示的时间实际上与我所在国家/地区的时间一致,所以看起来是当地时间,但服务器时区不同。

print_r(new DateTime());

上述语句的输出

DateTime Object ( [date] => 2020-07-25 12:40:33.451120 [timezone_type] => 3 [timezone] => Europe/Berlin )

在这两种情况下,记录来自数据库和日期函数,时区是欧洲/柏林,但 DateTime 函数根据服务器时间显示正确的日期。

(这些输出是在创建记录之后记录的,几乎没有任何时差)

为什么会这样? 我想根据创建时间和新的 DateTime 计算过期时间,即当前时间。

我是否应该不使用时间戳并根据 new DateTime() 插入创建日期;似乎根据服务器输出正确日期时间的函数。

最可靠的方法是什么?

【问题讨论】:

  • var_dump($row0['creation_date_time']); 的输出是什么?
  • 它的字符串(19) "2020-07-25 15:31:46"
  • 可以在DateTime的构造函数中指定时区。如果您不指定它并且输入日期没有指定时区,则将使用时区“UTC”。
  • 我已经用推荐更新了我的答案。

标签: php mysql


【解决方案1】:

如果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)。

【讨论】:

  • 奇怪的是,新的 DateTimeZone 并没有改变任何来自 mysql 的日期时间仍然在我的本地时区输出。可能是我的xampp安装问题。 DateTime 对象([date] => 2020-08-25 18:17:07.000000 [timezone_type] => 3 [timezone] => Europe/Berlin)
  • 您一直将creation_date_time 值存储为字符串文字,例如2020-07-01 14:22:31,这可能是柏林时间,对吗?如果服务器的时间不是柏林时间,那么时间戳将被转换为错误的 UTC 时间。假设当您检索该列并假设相同的服务器时区有效,它将被转换回您开始时使用的相同字符串文字,但当然会使用服务器的本地时区进行解释。当您在 PHP 中使用此字符串文字构造 DateTime 时,您将在 DateTime 上强制使用柏林时区,因此看起来没问题。
  • (续)因此,只要您只在 PHP 中进行日期比较,您可能就可以了。但是 TIMESTAMP 值根本上是错误的。如果您使用 MySql 通过在 creation_date_time 上使用 DATE_ADD 并与 NOW() 比较结果来测试过期,您会得到错误的结果。所有这一切都假设您使用柏林时间创建了日期文字,并且您的服务器使用的不是柏林时间。我的建议假设 creation_date_time 已正确初始化,如果服务器时区与 PHP 时区不匹配,则可能不是这种情况。
  • (续)假设我已经理解了所有内容,我可以告诉您如何更正当前的 creation_date_time 列,以便 (1) 它们存储正确的 UTC 时间并 (2) 在以下情况下返回正确的预期字符串值您已将“欧洲/柏林”设置为会话时区。
  • 服务器时间是柏林时间,当你做 echo new DateTime();没有任何时区,它以柏林时间输出时间。但是,当您从数据库中提取时间戳时,它不会转换为服务器时区,无论您做什么,它都保持不变。
猜你喜欢
  • 1970-01-01
  • 2014-01-09
  • 2020-05-16
  • 2016-03-06
  • 1970-01-01
  • 2010-11-19
  • 2012-08-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多