MySQLの困った仕様
まずはこれをご覧ください
require 'date' Date.parse('0000-00-00 00:00:00') rescue p $! #=> #<ArgumentError: invalid date>
これが意味するのは、「'0000-00-00 00:00:00'なんて日付は知らねー」ということです
普通はこんな日付を指定する方に問題があるので、
気にすることはないのですが、MySQLが絡むと話は変わってきます
MySQLのDATETIME型において、「未定義」あるいは「解釈不能」の意味で、
この値が格納されることがあります
これを日付型オブジェクトに変換しようとすれば、当然落ちます
MySQL/Rubyの作者もこの問題は認識しているようで、
そのためにMysql::Timeというクラスを用意しているそうです
ただ、DBIを経由する場合、Mysql::Timeを無視して、
Date.parseを走らせてしまうようなのですΣ(・ω・ノ)ノ
大雑把にしか読んでませんが、この辺が関係ありますかね
Re: Finding out what row DBI chokes on?
解決策としては・・・
1. '0000-00-00 00:00:00'という値を使わない
そもそも、この値はSQLの仕様外であり、MySQL独自の仕様です
であれば、(DBIを使うくらいだし)他DBとの互換性を考えて、
そもそもNULLを格納するようにするのが手かと
連携元側では意味のある値だとしても、こちら側ではNULLと同じ意味なので、
メインシステムを通す前に、強制的にUPDATEをかけて、とりあえずの対応としました
いくらMySQL独自の仕様とはいえ、「NULL」と「未定義」は違うのだから、
きちっと区別できるMysql::Timeを使う方法です
他のDBのことを考えなければこれも手です
私が開発しているシステムでは、DBのコネクションは抽象化されてるので、
内部実装だけDBIからMySQL/Rubyに書き換えれば、
メインロジックの修正が不要で、根本対応としてはこの方針で考えてます
最後に気になるのがRailsのことですが、
DBIとMySQL/Rubyが両方あったときに、
Railsがどっちを使っているかの問題かと
Rails単体で完結する場合、DateTimeで扱えない値は使えないはずなので、
'0000-00-00 00:00:00'が出てくることはないはずですが、
今回のように外部システムと連動する場合が厄介ですね
Rubyist Magazine - RubyOnRails を使ってみる 【第 1 回】
「るびま」で見る限り、DBIは不要というか、使ってないように見えますが・・・
機会があれば調べてみます
参考までに、同じような問題はJava(のMySQLドライバ)でも発生するらしく、
ドライバにパラメータを渡すことでNULLか'0001-01-01'にできるそうです
(デフォルトは例外をthrow)
なぜデフォルトで例外を投げるのかというと、
「0001-01-01としても、NULLとしても、元の意味と変わってしまうから」だそうです