MySQL插入到DATETIME:使用ISO :: 8601格式是否安全?

rai*_*7ow 16 mysql datetime iso8601

在我们的项目中,我们使用Zend Framework Model生成器,它生成类似这样的东西,将存储在DB(MySQL)中的属性设置为DATETIME字段:

public function setObjectDatetime($data) {
  if (! $data instanceof Zend_Date) { ... some conversion code ... }
  $this->objectDatetime = $data->toString(Zend_Date::ISO_8601);
}
Run Code Online (Sandbox Code Playgroud)

所以ISO :: 8601格式的字符串(例如'2012-06-15T18:33:00 + 03:00')实际上是作为属性存储的.

当我们尝试使用save此模型并将此字符串传递给MySQL(版本5.5.16)时会出现问题:它会引发警告,但仍会使用正确的结果插入/更新相应的行.很容易检查问题是由MySQL引起的,而不是某些驱动程序的行为:只是发出这样的查询...

UPDATE table_name SET datetime_field = '2012-06-15T18:33:00+03:00' WHERE id = 1;
Run Code Online (Sandbox Code Playgroud)

......其结果将是1 row affected, 1 warning,与

1264 | Out of range value for column 'dt' at row 1

警告(显示SHOW WARNINGS).

对我来说,phpMyAdmin根本没有显示任何警告; 并且所有服务器端代码都将此查询作为实体查询处理.)

所以问题是:我们是否应该将我们在模型中存储的内容重新格式化为另一种字符串格式(例如'YY-MM-dd HH:mm:ss'?)或者它是否只是一些奇怪的MySQL行为将被修复迟早?

rai*_*7ow 16

看起来这个问题的简短答案是"不,这不安全" - 这个结论是在MySQL shell的一系列实验之后得出的.尽管如此,仍然会欣赏更"理论"的答案.

显然,默认情况下,即使sql_mode设置为STRICT_ALL_TABLES,MySQL引擎(默认情况下)接受它作为Datetime文字也非常自由:不仅接受了各种分隔符,它们也可能不同:

INSERT INTO t(dt) VALUES('2012-01,03.04:05@06'); -- Query OK, 1 row affected

此外,如果字符串太短,它将用零填充...但可能会有惊喜:

INSERT INTO t(dt) VALUES('2012011'); -- 2020-12-01 01:00:00 is what's inserted

令人遗憾的是,字符串太长(当最后一个可解析数字后面跟着空格以外的东西)将在严格模式下被视为无效值:

mysql> INSERT INTO t(dt) VALUES('2012-06-27T05:25Z');
ERROR 1292 (22007): Incorrect datetime value: '2012-06-27T05:25Z' for column 'dt' at row 1
mysql> INSERT INTO t(dt) VALUES('2012-06-27T05:25');
Query OK, 1 row affected (0.10 sec)
Run Code Online (Sandbox Code Playgroud)

在传统模式下,解析更加轻松 - 但不是更精确; 此外,在严格模式下被认为是不正确的字符串会给出一些"无声警告",但操作会成功:

mysql> INSERT INTO t(dt) VALUES('2012-06-27T05:25Z');
Query OK, 1 row affected, 1 warning (0.10 sec)

mysql> SHOW WARNINGS;
+---------+------+---------------------------------------------+
| Warning | 1264 | Out of range value for column 'dt' at row 1 |
+---------+------+---------------------------------------------+

mysql> SELECT dt FROM t;
+---------------------+
| dt                  |
+---------------------+
| 2012-06-27 05:25:00 |
+---------------------+
Run Code Online (Sandbox Code Playgroud)

底线是我们必须重写一些与DAL相关的代码,以便日期(和日期时间)始终以"规范化"形式发送到DB.我想知道为什么我们必须这样做,而不是Zend_Db开发人员.但我认为那是另一个故事.)


aka*_*kou 5

默认情况下 MySQL 使用 ISO9075 格式的日期时间

第一个和第二个参数的可能值会产生几种可能的格式字符串(有关使用的说明符,请参阅 DATE_FORMAT() 函数描述中的表)。ISO 格式是指 ISO 9075,而不是 ISO 8601。

http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_get-format