如何在 MySQL 数据库中存储 UTC ISO8601 日期?

Posted

技术标签:

【中文标题】如何在 MySQL 数据库中存储 UTC ISO8601 日期?【英文标题】:How do I store an UTC ISO8601 date in a MySQL database? 【发布时间】:2011-12-24 06:01:04 【问题描述】:

我有数千个日期,格式如下:

2011-10-02T23:25:42Z(在 UTC 中也称为 ISO 8601)

我应该使用哪种 mysql 数据类型在 MySQL 数据库中存储这样的 ISO8601 日期?例如。 Datetimetimestamp 还是别的什么?

哪个最适合比较(例如,获取两个日期/时间之间的记录)并对查询结果进行排序?如果数据库很大怎么办?

将上述 php 字符串转换为 MySQL 存储的最佳方法是什么? (我猜date_default_timezone_set('UTC'); 会被使用?)

【问题讨论】:

看看这个问题:***.com/questions/409286/datetime-vs-timestamp 这对我的情况有帮助:STR_TO_DATE('2016-10-19T00:57:38+0000','%Y-%m-%dT%H:%i:%s+%x' ) +%x 是因为 php 的 DateTime::ISO8601 中的时区数字,例如。 +0000 似乎在 mysql 中没有指定的表示,因此使用 %x 表示未知,甚至 STR_TO_DATE('2016-10-19T00:57:38+0000','%Y-%m-%dT%T') 但它引发截断日期时间 ps 的警告:对于 '2016-10-19T00:57:38+0000',使用 '%Y-%m-%dT%H:%i:%s+%x' 作为 mysql 中的格式说明符跨度> 【参考方案1】:

在我的系统上使用您的日期时间,即 PDT:

SELECT CONVERT_TZ(str_to_date('2011-10-02T23:25:42Z','%Y-%m-%dT%H:%i:%sZ'),'+00:00','SYSTEM') from dual;

2011-10-02 16:25:42

如果您的日期时间有小数微秒;在 Z 之前包含 .%f,如下所示:

SELECT CONVERT_TZ(str_to_date('2011-10-02T23:25:42.123456Z','%Y-%m-%dT%H:%i:%s.%fZ'),'+00:00','SYSTEM') from dual;

2011-10-02 16:25:42.123456

【讨论】:

【参考方案2】:

您不能以原始 UTC ISO8601 格式(使用2011-10-02T23:25:42Z 表示)存储日期并保存所有 SQL DATETIME 功能。

但您应该知道,MySQL(关于 http://dev.mysql.com/doc/refman/5.5/en/datetime.html )总是以 UTC 存储时间/日期。 您还可以为您的连接修改时区 http://dev.mysql.com/doc/refman/5.5/en/time-zone-support.html

所以,如果你在 PHP 中执行

date_default_timezone_set('UTC');

在 MySQL 中

SET time_zone = +00:00

确定 PHP 和 MySQL 会使用 UTC。

之后,您可以将所有数据库字符串转换为 DateTime,而无需担心时区不匹配。

要将任何 PHP DateTime(不带其内部时区)转换为 MySQL 日期时间字符串,您应该将 DateTime 对象时区设置为 UTC。

$datetime->setTimezone(new DateTimeZone('UTC'))->format('Y-m-d H:i:s');

【讨论】:

【参考方案3】:

您可以使用phpstrtotime 函数轻松转换日期:

date_default_timezone_set('UTC');
$date = '2011-10-02T23:25:42Z';//(aka ISO 8601 in UTC)
$time = strtotime($date); //time is now equals to the timestamp
$converted = date('l, F jS Y \a\t g:ia', $time); //convert to date if you prefer, credit to Marc B for the parameters

现在您只需使用timestampdatetimeMySQL 中插入您的日期,具体取决于哪一个最适合您的需求。这是您应该了解的关于这两种类型的最重要的事情。


时间戳

从 '1970-01-01 00:00:01' UTC 到 '2038-01-09 03:14:07' UTC 的范围 受时区设置影响。 4 字节存储 在所有版本的列上允许on update current_timestamp。 索引速度更快 NULL 不是可能的默认值 值从当前时区转换为UTC 进行存储,并从UTC 转换回当前时区以进行检索。

日期时间

范围从“1000-01-01 00:00:00”到“9999-12-31 23:59:59” 恒定(时区不受影响) 8 字节存储 仅允许更新自版本 5.6.5 起的列

哪个最适合比较(例如,获取两个之间的记录 日期/时间)并从查询中排序结果?如果 数据库很大吗?

根据我前面的几点,那么对于非常大的数据库,您应该使用timestamp,因为存储空间更小,索引更快,这将为您提供更好的比较性能。但是,您必须确保您的日期符合我之前提到的 timestamp 的限制,否则您别无选择,必须使用 datetime

strtotime 的文档:http://php.net/manual/en/function.strtotime.php

并且,为了每天不断重复不使用mysql* 已弃用 功能的 SO 回答者,请在您插入时使用 PDOmysqli*

http://php.net/manual/en/book.pdo.php

http://php.net/manual/en/book.mysqli.php

【讨论】:

【参考方案4】:

我认为将日期时间值保存在 DATETIME 类型的字段中是一种自然的方式。

根据我对当前 PHP 应用程序的经验,只有与此信息有关的 read / write 操作可能会出现问题。

正确执行整个过程的一种可能的解决方案(假设您使用DATETIME 数据类型)可能是以下方法:

读取 DATETIME 值以供 PHP 使用

    使用DATE_FORMAT MySQL 函数和'%Y-%m-%dT%H:%i:%sZ' 格式化字符串( docs on DATE_FORMAT) 以这种特定格式读取获取的列值,并将其在 PHP 中从字符串转换为对 PHP 有效的实际日期时间表示(例如DateTime 类对象和DateTime::createFromFormat 静态方法给定'Y-m-d\TH:i:s\Z' 格式化字符串(@987654336 @ 和 Z 被转义以避免将它们视为格式化指令)(docs for the method)。 将转换后的值用作具有所有适用逻辑的真实日期时间值,例如真实日期比较(不是文本比较)等。

将 PHP 日期时间写入 MySQL 数据库

    使用DateTime类对象的format方法将PHP DateTime类对象转换为我们的ISO 8601格式字符串表示,与之前的'Y-m-d\TH:i:s\Z'格式字符串相同(documentation)。 使用准备好的字符串作为MySQL函数STR_TO_DATE(带有'%Y-%m-%dT%H:%i:%sZ'格式化字符串)的参数对数据库信息执行INSERT/UPDATE操作,将其转换为真实数据库DATETIME值( docs on STR_TO_DATE)。

PHP 中的示例代码

请在下面找到使用 PDO 对象的此类方法的示例草稿:

$db = new PDO('mysql:host=localhost;dbname=my_db;charset=utf8', 'username', 'password');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

try 
    // run the query aquring 1 example row with DATETIME data 
    // converted with MySQL DATE_FORMAT function to its string representation 
    // in the chosen format (in our case: ISO 8601 / UTC)
    $stmt = $db->query("SELECT DATE_FORMAT(dt_column, '%Y-%m-%dT%H:%i:%sZ') AS formatted_dt_col"
                        ." FROM your_table LIMIT 1"); 

    if($stmt !== FALSE) 
        $row = $stmt->fetch(PDO::FETCH_ASSOC);

        // convert the acquired string representation from DB 
        // (i.e. '2011-10-02T23:25:42Z' )
        // to PHP DateTime object which has all the logic of date-time manipulation:    
        $dateTimeObject = DateTime::createFromFormat('Y-m-d\TH:i:s\Z', $row['formatted_dt_col']);

        // the following should print i.e. 2011-10-02T23:25:42Z
        echo $dateTimeObject->format('Y-m-d\TH:i:s\Z');  

        // now let's write PHP DateTime class object '$dateTimeObject' 
        // back to the database
        $stmtInsertDT = $db->prepare("INSERT INTO your_table(dt_column) " 
                             . " VALUES ( STR_TO_DATE(:par_formatted_dt_column, '%Y-%m-%dT%H:%i:%sZ') )");

        $dtAsTextForInsert = $dateTimeObject->format('Y-m-d\TH:i:s\Z');

        // convert '$dateTimeObject' to its ISO 8601 / UTC text represantation
        // in order to be able to put in in the query using PDO text parameter
        $stmtInsertDT->bindParam(':par_formatted_dt_column', $dtAsTextForInsert, PDO::PARAM_STR);

        $stmtInsertDT->execute();

        // So the real insert query being perform would be i.e.:
        /*
           INSERT INTO your_table(dt_column) 
           VALUES ( STR_TO_DATE('2011-10-02T23:25:42Z', '%Y-%m-%dT%H:%i:%sZ') )
        */
    

catch(\PDOException $pexc) 
 // serve PDOException

catch(\Exception $exc) 
// in case of no-PDOException, serve general exception

这种方法对我在 PHP 和 MySQL 数据库之间操作日期时间值有很大帮助。

我希望它对你也有帮助。

【讨论】:

Ashish 使用 CAST 的解决方案似乎是更好/更简单的转换方式。 好的。没有恶意。这完全是您的选择。问候。 我只是想知道您是否有理由不这样做。【参考方案5】:

您可以使用DateTime 数据类型来存储日期和时间。

使用CAST函数将此类字符串转换为mysqlDateTime类型。

这是一个例子:

CAST("2011-10-02T23:25:42Z" AS DATETIME)

这会给你2011-10-02 23:25:42

希望这会对你有所帮助。

【讨论】:

插入 CAST('2020-01-08T06:28:43Z' AS DATETIME), 给我一个错误 #1292 - Truncated incorrect datetime value: '2020-01-08T06:28:43Z' CAST('2020-05-19T19:03:56+02:00' AS DATETIME)一样...不行,我必须先删除+02:00,然后它才能工作【参考方案6】:

以下是使用日期时间更好的原因。

    使用 datetime 您将能够在 mysql 端进行日期操作 - 例如减去日、月 您将能够对数据进行排序。 如果 DB 很大 - varchar 会在 HDD 上占据更多位置

【讨论】:

当我将 '2011-10-02T23:25:42Z' 插入 DB 时,它给了我一个警告:#1264 第 1 行的列 'tz' 的值超出范围 @user967144 列 tz 应该是 'datetime' 类型,并且您传递的插入值应该使用 php date('Y-m-d H:i:s',strtotime('2011-10-02T23:25:42Z')).. 希望这会有所帮助 @user967144 请注意,日期末尾的字母“Z”会使 PHP 将时间转换为服务器的时区。如果要存储 GMT 时间,则必须删除'Z' 从日期结束... 你仍然可以插入字符串,只要你使用 MySQL 的 STR_TO_DATE('2011-10-02T23:25:42Z', '%Y-%m-%dT%TZ') 让 MySQL将其转换为日期时间。 (见dev.mysql.com/doc/refman/5.5/en/…) @RolandBouman 感谢您的提示。上面的格式字符串在带有小数秒的 ISO 日期上返回 null。但是这种格式 - 从 dual 中选择 str_to_date('2014-03-21T22:27:22.392423Z' , '%Y-%m-%dT%H:%i:%s' );工作。

以上是关于如何在 MySQL 数据库中存储 UTC ISO8601 日期?的主要内容,如果未能解决你的问题,请参考以下文章

将存储在mysql(utc格式)中的momentjs datetime格式化为Local DateTime

如何将MySQL Datetime UTC转换为实际时区

如何使用 JPA 和 Hibernate 在 UTC 时区中存储日期/时间和时间戳

如何将 MySQL 数据库更改为 UTC?

如何将存储为 VARCHAR 的 UTC 日期时间值转换为常规日期时间

当存储为 UTC 时,如何全局序列化和反序列化 Date 与 DateTime?