Yii1: исправить ошибку DATETIME с CURRENT_TIMESTAMP
Решение ошибки Incorrect datetime value 'current_timestamp()' в Yii1 при вставке в DATETIME. Не присваивайте строку — используйте CDbExpression('NOW()') или DEFAULT в схеме.
В Yii1 при добавлении записи без указания значений полей типа DATETIME вставляется строка ‘current_timestamp()’ вместо актуального значения, что вызывает ошибку: CDbCommand не удалось исполнить SQL-запрос: SQLSTATE[22007]: Invalid datetime format: 1292 Incorrect datetime value: ‘current_timestamp()’. Как исправить эту проблему?
DDL полей:
date_create DATETIME DEFAULT CURRENT_TIMESTAMP,
date_update DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
Проект унаследован, код писал не я.
В Yii1 проблема с полями DATETIME возникает потому, что приложение подставляет строку ‘current_timestamp()’ (в кавычках) вместо SQL‑выражения, из‑за чего MySQL бросает ошибку 1292. Исправление — не передавать строковый литерал и/или использовать CDbExpression (например, new CDbExpression(‘NOW()’) или new CDbExpression(‘CURRENT_TIMESTAMP’)) в правилах или beforeSave, либо позволить СУБД проставлять DEFAULT CURRENT_TIMESTAMP корректно на уровне схемы. После этого INSERT будет содержать CURRENT_TIMESTAMP без кавычек и ошибка исчезнет.
Содержание
- Причина: почему в Yii1 DATETIME получает ‘current_timestamp()’
- Исправление в коде: используем CDbExpression в Yii1 для DATETIME
- Примеры: rules() и beforeSave() в Yii1
- Проверка DDL и MySQL: DATETIME vs TIMESTAMP
- Отладка и тесты — как убедиться, что всё починили
- Источники
- Заключение
Причина: почему в Yii1 DATETIME получает ‘current_timestamp()’
Коротко: причина в том, что в INSERT в базу попадает строка ‘current_timestamp()’ (с кавычками), а не SQL‑функция CURRENT_TIMESTAMP. Почему так происходит? Чаще всего потому, что в коде модели или в правилах/контроллере атрибуту присвоили строковое значение “current_timestamp()” или генерировали DDL/миграцию, где DEFAULT указан в кавычках. Тогда PDO/Yii привязывают значение как строку и MySQL пытается распарсить её как дату — получает ошибку SQLSTATE[22007]: 1292.
Такое поведение обсуждалось в репозитории Yii (рекомендация — использовать CDbExpression для выражений) — см. обсуждение на GitHub: https://github.com/yiisoft/yii/issues/2993. Кроме того, есть багрепорты и примеры, когда неверно сгенерирован DDL (кавычки вокруг current_timestamp()), что тоже даёт похожую ошибку (см. https://bugs.mysql.com/bug.php?id=95466 и https://github.com/phpmyadmin/phpmyadmin/issues/13999).
Что делать сначала? Проверьте код на наличие буквальной строки “current_timestamp” и вывод SQL (ниже — как отладить).
Исправление в коде: используем CDbExpression в Yii1 для DATETIME
Есть два безопасных подхода — дать СУБД проставлять значение по умолчанию или явно подставлять SQL‑выражение из приложения (без кавычек).
- Не назначать значение в модели, чтобы сработал DEFAULT базы.
- Убедитесь, что в PHP вы не присваиваете полю строку ‘current_timestamp()’.
- Если код раньше выставлял такое значение (в контроллере/правилах/миграциях) — удалите это присвоение.
- Если хотите ставить метку времени из приложения — используйте CDbExpression, тогда Yii не обернёт выражение в кавычки и в SQL попадёт корректное выражение (NOW() или CURRENT_TIMESTAMP). Пример решения рекомендуется в сообществе и в вики Yii: https://www.yiiframework.com/wiki/10/how-to-automate-timestamps-in-activerecord-models и на StackOverflow (см. примеры: https://stackoverflow.com/questions/31743439/insert-automatically-datetime-in-yii).
Главная идея: вместо
- $model->date_create = ‘current_timestamp()’;
поставить
- $model->date_create = new CDbExpression(‘NOW()’);
или использовать правило default с CDbExpression (см. снизу).
Примеры: rules() и beforeSave() в Yii1
Пример через правила модели (recommended):
public function rules()
{
return array(
// при создании выставляем NOW()
array('date_create, date_update', 'default',
'value' => new CDbExpression('NOW()'),
'setOnEmpty' => false,
'on' => 'insert'),
// при обновлении обновляем date_update
array('date_update', 'default',
'value' => new CDbExpression('NOW()'),
'setOnEmpty' => false,
'on' => 'update'),
// ... другие правила
);
}
Альтернатива — переопределить beforeSave():
protected function beforeSave()
{
if (parent::beforeSave()) {
if ($this->isNewRecord) {
$this->date_create = new CDbExpression('NOW()');
}
$this->date_update = new CDbExpression('NOW()');
return true;
}
return false;
}
Или подключить поведение (CTimestampBehavior или аналог) и задать timestampExpression => new CDbExpression(‘NOW()’) — так код станет компактнее. Примеры и обсуждения поведения есть в вики и на форумах Yii (см. https://www.yiiframework.com/wiki/10/how-to-automate-timestamps-in-activerecord-models и документацию Yii2 для идеи поведения: https://www.yiiframework.com/doc/api/2.0/yii-behaviors-timestampbehavior).
Почему CDbExpression помогает? Потому что Yii вставляет выражение в SQL “как есть” (без кавычек), поэтому MySQL получает CURRENT_TIMESTAMP или NOW(), а не строковый литерал.
Проверка DDL и MySQL: DATETIME vs TIMESTAMP
Ваша DDL:
date_create DATETIME DEFAULT CURRENT_TIMESTAMP,
date_update DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
Важно знать: поддержка DEFAULT CURRENT_TIMESTAMP для полей DATETIME появилась в MySQL 5.6. Если у вас версия ниже — DATETIME не поддерживает такой default (а TIMESTAMP — да). Проверьте версию сервера:
SELECT VERSION();
Проверьте, как реально записан default в схеме:
SHOW CREATE TABLE your_table \G
Обратите внимание: должно быть именно DEFAULT CURRENT_TIMESTAMP (без кавычек). Если в SHOW CREATE TABLE вы видите DEFAULT ‘current_timestamp()’ — это ошибка (кавычки), и нужно изменить схему. Пример ALTER для MySQL >= 5.6:
ALTER TABLE your_table
MODIFY date_create DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
MODIFY date_update DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;
Если MySQL < 5.6 и вы не хотите апгрейдить, можно заменить DATETIME на TIMESTAMP:
ALTER TABLE your_table
MODIFY date_create TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
MODIFY date_update TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;
Учтите разницу: TIMESTAMP хранит значение с учётом временной зоны соединения (может происходить конвертация), DATETIME — просто набор значений года/месяца/дня/времени (без конверсии). Выберите тип, исходя из требований проекта.
Подробности и примеры ошибок при некорректном прописывании DEFAULT — в багрепортах MySQL и обсуждениях (см. https://bugs.mysql.com/bug.php?id=95466 и https://github.com/phpmyadmin/phpmyadmin/issues/13999).
Отладка и тесты — как убедиться, что всё починили
Найдите в кодовой базе все вхождения “current_timestamp” (чтобы понять, кто подставляет строку):
grep -R --exclude-dir=vendor -n "current_timestamp" .
Включите логирование параметров и профайлинг в конфиге DB (для проверки реального SQL):
'db' => array(
'connectionString' => 'mysql:host=...;dbname=...',
'username' => '...',
'password' => '...',
'enableParamLogging' => true,
'enableProfiling' => true,
),
После этого посмотрите runtime/logs/app.log или используйте дебаг-панель (если есть). Неправильный SQL выглядит так:
INSERT INTO t (date_create) VALUES (‘current_timestamp()’);
Правильный — так:
INSERT INTO t (date_create) VALUES (CURRENT_TIMESTAMP);
Также протестируйте сценарии:
- Создание записи без явного присвоения поля — СУБД должна проставить значение.
- Создание записи с CDbExpression — проверьте, что в SQL попадает выражение.
Наконец, прогоните основные рабочие сценарии и проверьте отображение времени (возможны нюансы с часовыми поясами при смене TIMESTAMP/DATETIME).
Источники
- Обсуждение ошибки и рекомендация CDbExpression: https://github.com/yiisoft/yii/issues/2993
- Пример ошибки “Invalid datetime format” (StackOverflow): https://stackoverflow.com/questions/45485242/mysql-invalid-datetime-format
- Вики Yii: как автоматизировать timestamp в ActiveRecord: https://www.yiiframework.com/wiki/10/how-to-automate-timestamps-in-activerecord-models
- Документация о поведении Timestamp (Yii2, для идеи): https://www.yiiframework.com/doc/api/2.0/yii-behaviors-timestampbehavior
- Примеры на StackOverflow (автоматическая вставка datetime в Yii): https://stackoverflow.com/questions/31743439/insert-automatically-datetime-in-yii
- Сохранение и вывод даты/времени (Yii2, см. идеи): https://www.yiiframework.com/wiki/684/save-and-display-datetime-fields-in-different-formats-in-yii2
- Пример использования default + CDbExpression (StackOverflow): https://stackoverflow.com/questions/18434575/to-store-datetime-automatically-using-yii
- Проблемы с defaultValue CURRENT_TIMESTAMP в миграциях (Yii2): https://github.com/yiisoft/yii2/issues/9343
- Похожие случаи с некорректными datetime в MySQL (DBA.SE): https://dba.stackexchange.com/questions/234270/incorrect-datetime-value-mysql
- Баг MySQL про кавычки и CURRENT_TIMESTAMP: https://bugs.mysql.com/bug.php?id=95466
- Ещё багрепорт MySQL (описание): https://bugs.mysql.com/bug.php?id=96585
- Проблема phpMyAdmin генерации DDL с кавычками: https://github.com/phpmyadmin/phpmyadmin/issues/13999
- Дополнительная дискуссия про ошибку “Incorrect Datetime value for CURRENT_TIMESTAMP”: https://stackoverflow.com/questions/62995233/sql-incorrect-datetime-value-for-current-timestamp-sometimes
Заключение
Если кратко: не подавайте в INSERT строку ‘current_timestamp()’ — либо уберите присвоение и дайте СУБД проставить DEFAULT CURRENT_TIMESTAMP, либо в коде используйте CDbExpression (new CDbExpression(‘NOW()’) / new CDbExpression(‘CURRENT_TIMESTAMP’)), чтобы Yii не экранировал выражение. Проверьте SHOW CREATE TABLE и версию MySQL (DATETIME DEFAULT CURRENT_TIMESTAMP требует MySQL ≥ 5.6) и выполните тесты после правок — проблема исчезнет.