如何在 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 日期?例如。 Datetime
、timestamp
还是别的什么?
哪个最适合比较(例如,获取两个日期/时间之间的记录)并对查询结果进行排序?如果数据库很大怎么办?
将上述 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】:您可以使用php
的strtotime
函数轻松转换日期:
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
现在您只需使用timestamp
或datetime
在MySQL
中插入您的日期,具体取决于哪一个最适合您的需求。这是您应该了解的关于这两种类型的最重要的事情。
时间戳
从 '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 回答者,请在您插入时使用 PDO
或 mysqli*
。
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' 类型,并且您传递的插入值应该使用 phpdate('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
如何使用 JPA 和 Hibernate 在 UTC 时区中存储日期/时间和时间戳