Nekoya Press

2013-07-03 Wed 10:32

MySQL-python1.2.4でdatetimeから文字列の変換方法が変わった件

MySQL-python(MySQLdb)を上げたら、今まで通ってたテストでwarningが出るようになりました。

MySQLdb/times.pyで以前は

def format_TIMESTAMP(d):
    return d.strftime("%Y-%m-%d %H:%M:%S")

こうなっていたものが、

def format_TIMESTAMP(d):
    return d.isoformat(" ")

こうなったのが原因らしいのだけど、Changelogを見てもそれらしい表記がないので少し経緯を追ってみました。

この変更で何が問題になったかというと、

>> import datetime, pytz
>>> datetime.datetime.now(pytz.utc).isoformat(' ')  # aware
'2013-06-27 01:50:16.156481+00:00'
>>> datetime.datetime.now().isoformat(' ')  # naive
'2013-06-27 10:48:30.696072'

のように、microsecondや時差を含んだ文字列がSQLに渡って「Warning: Incorrect datetime value: '2013-06-27 01:50:16.156481+00:00' for column 〜」という警告が出るようになりました。

履歴を追っていくと、http://sourceforge.net/p/mysql-python/svn/659/

Use isoformat() instead of strftime() to avoid year limitations of the latter. Fixes #3296395

とあって、1900年以前の日付を扱えるようにこの変更を加えたことが分かります。strftime(3)の仕様的にそうなってたけど、そこの制限を外したぜってことらしいです。だけど、この#3296395ってどこのことだ…

該当するチケットは #311 executing a datetime column update with year < 1900 fails っぽいけど、BTS移行とかしたんでしょうか。

最初はMySQL5.6でdatetime型がミリ秒対応したらしい(検証してない)ので、それに合わせた施策かなーと思ったらそんなことはなかった。

他にもmicrosecond周りでは #325 Datetime fields with microsecond shows as None みたいなチケットが今も動いていたりして、なかなか予断を許さない印象を受けます。

DBのライブラリがdatetimeオブジェクトを適切に扱ってくれるのは素敵だと思ってたけど、なかなか難しいですね。取り急ぎの対処法としては、

  • microsecondやtzinfoを含まないdatetimeオブジェクトだけを扱う
  • 事前に自分で文字列にして渡す

のいずれかにわけで、自分のユースケースだとコネクション管理とSQL::Makerの簡易版みたいなことをするラッパーを通しているので、そこで時前でstrftimeして乗り切る感じになりました。

メロスには英語がわからぬ。だが、issueは切ってみた。

nekoya.github.io