18

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

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

所以 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;

...结果将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 的一些奇怪行为将被修复迟早?

4

3 回答 3

19

看起来这个问题的简短回答是“不,这不安全”——这个结论是在一系列 MySQL shell 实验之后得出的。不过,仍然会欣赏一个更“理论”的答案......

显然,即使设置为 STRICT_ALL_TABLES,MySQL 引擎(默认情况下)在它接受的日期时间文字方面也非常自由sql_mode:不仅接受各种分隔符,它们也可能不同:

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)

在传统模式下,解析更加轻松——但不是更精确;此外,在严格模式下被认为不正确的字符串会给出某种“无声警告”,尽管操作会成功:

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 |
+---------------------+

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

于 2012-06-27T17:50:03.723 回答
4

据我所知,没有办法在 MySQL Date 或 Time 类型中存储时间偏移信息(ISO 8601 字符串末尾的 +03:00),因此您只能靠自己来寻找解决方案。

一种可能的方法是拆分 ISO 8601 字符串并将偏移量存储在 char(5) 列中,尽管不可否认这会使其有点难以使用。我想您可以将偏移量存储在时间列中,这可能会使日期/时间操作更容易一些。

编辑
我刚刚在 MySQL 文档中偶然发现了这一点,这可能会有所帮助。

于 2012-06-15T15:46:47.527 回答
3

默认情况下 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

于 2015-02-04T14:32:19.700 回答