◀ 8.

Cannot convert value YYYY-MM-DD to TIMESTAMP 対処

▶
この記事の要点
  • MySQL の DATETIME / TIMESTAMP 列から、Java(JDBC)が 日時として解釈できない値を読んだときに出るエラー
  • 最も多い原因は 0000-00-00 00:00:00 のようなゼロ日付。Java の日時型には存在しない日付
  • 手早い対処は接続 URL に zeroDateTimeBehavior を付けて NULL に変換させること(8.x 以降は CONVERT_TO_NULL)
  • 根本対処は、ゼロ日付を NULL などにデータ修正し、sql_mode でゼロ日付の混入を防ぐこと
  • 非常に古い MySQL Connector/J(3 系など)の不具合でも出るため、ドライバの更新も検討する

エラー内容

java.sql.SQLException: Cannot convert value 'YYYY-MM-DD ...' from column n(YYYY-MM-DD ...) to TIMESTAMP.

ResultSet#getTimestamp() や、Hibernate / MyBatis などの O/R マッパーが結果をエンティティに詰めるときに発生します。'YYYY-MM-DD ...' の部分には実際の値(例: '0000-00-00 00:00:00')、column n には何列目かが表示されます。

原因

原因 1: 有効な日時でない値(ゼロ日付)が入っている

MySQL は設定によって 0000-00-00 や 2024-02-00 のような「存在しない日付」を DATETIME / TIMESTAMP / DATE 列に保存できます。古いアプリが「未設定」の意味でゼロ日付を入れていることがよくあります。

しかし Java の java.sql.Timestamp や LocalDateTime には 0 年 0 月 0 日という日付が存在しないため、ドライバは変換できずに例外を投げます。0 埋めの値に限らず、年・月・日のいずれかが 0 の値(例: 2020-00-15)でも同じです。

原因 2: MySQL Connector/J が非常に古い

Connector/J の 3 系のような古いドライバでは、日時の変換に関する不具合や、ゼロ日付の扱いの違いでこのエラーが出ることがあります。古いシステムを保守している場合は、ドライバのバージョンも確認してください。

対処法 1: 接続 URL に zeroDateTimeBehavior を付ける

Connector/J には、ゼロ日付を読んだときの動作を決める接続プロパティ zeroDateTimeBehavior があります。NULL に変換させれば、例外を出さずに読めるようになります。

Connector/J のバージョンNULL に変換する値その他の値
8.x 以降(現行の mysql-connector-j)CONVERT_TO_NULLEXCEPTION(既定)/ ROUND
5.1 系convertToNullexception(既定)/ round
# Connector/J 8.x 以降
jdbc:mysql://localhost:3306/mydb?zeroDateTimeBehavior=CONVERT_TO_NULL

# Connector/J 5.1 系
jdbc:mysql://localhost:3306/mydb?zeroDateTimeBehavior=convertToNull

Spring Boot なら application.properties の spring.datasource.url にこのパラメータを付けます。既にパラメータがある場合は、? ではなくアンパサンド記号でつなげてください。

ROUND を指定すると、ゼロ日付は 0001-01-01 00:00:00 に丸められます。NULL を受け付けないフィールドがある場合の選択肢ですが、「本当の日付」と区別しにくくなるため、通常は NULL 変換の方が扱いやすいでしょう。

対処法 2: データを直す(根本対処)

接続 URL での回避は読み込み時の変換にすぎず、ゼロ日付はデータベースに残り続けます。他のアプリや集計で同じ問題が起きないよう、可能ならデータ自体を修正します。

-- ゼロ日付の件数を確認
SELECT COUNT(*) FROM orders WHERE shipped_at = '0000-00-00 00:00:00';

-- 列が NULL を許可していなければ、先に許可する
ALTER TABLE orders MODIFY shipped_at DATETIME NULL;

-- ゼロ日付を NULL に置き換える
UPDATE orders SET shipped_at = NULL WHERE shipped_at = '0000-00-00 00:00:00';

sql_mode に NO_ZERO_DATE などが含まれていると、WHERE 句のゼロ日付リテラル自体が警告やエラーになる場合があります。その場合は、CAST(shipped_at AS CHAR) = '0000-00-00 00:00:00' のように文字列として比較する方法もあります。本番データを更新する前には必ずバックアップを取り、件数を確認してから実行してください。

対処法 3: ゼロ日付が入らないようにする

MySQL 5.7 以降の既定の sql_mode には NO_ZERO_DATE と NO_ZERO_IN_DATE(厳格モードと組み合わせて有効)が含まれており、新たにゼロ日付を保存しようとするとエラーになります。古いバージョンから移行してきたサーバーや、アプリ側で sql_mode を緩めている場合は、この設定が外れていないか確認します。

SELECT @@GLOBAL.sql_mode, @@SESSION.sql_mode;

アプリ側でも「未設定」を表すにはゼロ日付ではなく NULL を使うように修正すると、同じ問題の再発を防げます。

対処法 4: ドライバを更新する

Connector/J が古い場合は、使っている MySQL サーバーと Java のバージョンに対応した現行のドライバに更新します。Maven では 8.0.31 以降、座標が com.mysql:mysql-connector-j に変わっている点に注意してください(旧座標は mysql:mysql-connector-java)。ドライバのクラス名も 8 系では com.mysql.cj.jdbc.Driver です。

よくある落とし穴

  • DATE 列でも起きる: 0000-00-00 の DATE 列を getDate() で読むと同様のエラーになる
  • プロパティ値の大文字小文字: 8.x 以降と 5.1 系で値の書き方が違う。ドライバのバージョンを確認してから指定する
  • NULL 変換後の NullPointerException: NULL に変換されるようになったことで、今度はアプリ側で null を想定していない箇所が落ちることがある
  • コピー先 DB では再現しない: ダンプ・リストア時に sql_mode の違いでゼロ日付が変換・拒否され、検証環境では再現しないことがある

確認方法

  1. エラーメッセージの列番号から、該当テーブル・列を特定する
  2. SELECT ... WHERE 列 = '0000-00-00 00:00:00' などでゼロ日付の行があるか調べる
  3. zeroDateTimeBehavior を付けて再実行し、例外が出ずに null として取得できるか確認する
  4. データ修正後は、パラメータを外しても例外が出ないことを確認する

関連

Post Share
子ページ

子ページはありません

同階層のページ
  1. java.lang.NoSuchMethodError
  2. java.lang.ClassCastException: java.util.Date cannot be cast to java.sql.Date
  3. java.lang.UnsupportedClassVersionError
  4. version less than X.X is not supported.
  5. パッケージ~は存在しません
  6. org.apache.jasper.JasperException: ...The jsp:param action must not be...
  7. java.io.FileNotFoundException: ファイル名 (許可がありません)
  8. java.sql.SQLException: Cannot convert value 'YYYY-MM-DD ...' from column n(YYYY-MM-DD ...) to TIMESTAMP.
  9. 警告: この文字は、エンコーディング[文字コード]にマップできません
  10. java.text.ParseException: Unparseable date
  11. Unsupported major.minor version 52.0
  12. エンティティ" ... "への参照は';'デリミタで終了する必要があります。
  13. java.math.BigDecimal cannot be cast to java.lang.String