【问题标题】:How do I store an UTC ISO8601 date in a MySQL database?如何在 MySQL 数据库中存储 UTC ISO8601 日期?
【发布时间】: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'); 会被使用?)

【问题讨论】:

  • 这对我的情况有帮助: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 中的格式说明符跨度>

标签: php mysql date date-format iso8601


【解决方案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 使用

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

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

        1. 使用DateTime类对象的format方法将PHP DateTime类对象转换为我们的ISO 8601格式字符串表示,与之前的'Y-m-d\TH:i:s\Z'格式字符串相同(documentation)。
        2. 使用准备好的字符串作为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】:

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

        1. 使用 datetime 您将能够在 mysql 端进行日期操作 - 例如减去日、月
        2. 您将能够对数据进行排序。
        3. 如果 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' );工作。
        猜你喜欢
        • 2022-01-08
        • 2011-04-17
        • 2014-05-04
        • 2018-09-28
        • 2015-06-20
        • 2011-02-04
        • 1970-01-01
        • 2014-11-01
        相关资源
        最近更新 更多