MySQL вставляет в DATETIME: безопасно ли использовать формат ISO:: 8601? - программирование
Подтвердить что ты не робот

MySQL вставляет в DATETIME: безопасно ли использовать формат ISO:: 8601?

В нашем проекте мы используем генератор 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);
}

Таким образом, форматированная строка 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).

К моему amuzement, phpMyAdmin вообще не показывает никаких предупреждений; и весь серверный код обработал этот запрос как сплошной. )

Итак, вопрос в том, должен ли мы действительно переформатировать то, что мы храним в нашей Модели, в другой строковый формат (например, "YY-MM-dd HH: mm: ss"?) Или это просто какое-то странное поведение MySQL, будет исправлено рано или поздно?

4b9b3361

Ответ 1

Похоже, что короткий ответ на этот вопрос: "Нет, это небезопасно" - этот вывод следует серии экспериментов с оболочкой MySQL. Все равно был бы оценен более "теоретический" ответ, хотя...

По-видимому, MySQL-движок (по умолчанию) довольно либеральный в том, что он принимает как литерал Datetime, даже если sql_mode установлен в STRICT_ALL_TABLES: не только разные разделители приняты, они могут также отличаться:

INSERT INTO t(dt) VALUES('2012-01,03.04:[email protected]'); -- Query OK, 1 row affected

Кроме того, если строка слишком короткая, она будет дополнена нулями... но могут быть сюрпризы:

INSERT INTO t(dt) VALUES('2012011'); -- 2020-12-01 01:00:00 is what 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, так что даты (и datetimes) всегда отправляются в БД в "нормализованной" форме. Интересно, почему это нам нужно, а не разработчикам Zend_Db. Но эта другая история, я полагаю. )

Ответ 2

Насколько я знаю, нет способа хранить информацию о смещении времени (+03: 00 в конце строки ISO 8601) в типах MySQL Date или Time, поэтому вы сами можете найти решение.

Одним из возможных подходов является разделение строки ISO 8601 и сохранение смещения в столбце char (5), хотя, по общему признанию, это затруднит работу. Я полагаю, вы можете сохранить смещение в столбце "Время", что может облегчить манипуляции с датами/временем.

ИЗМЕНИТЬ
Я просто наткнулся на этот в документах MySQL, что может быть полезно.

Ответ 3

По умолчанию MySQL использует формат ISO9075 для datetime

Возможные значения для первого и второго аргументов приводят к нескольким возможным строкам формата (для используемых спецификаторов см. таблицу в описании функции DATE_FORMAT()). Формат ISO относится к ISO 9075, а не ISO 8601.

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