Re: Time::Piece::MySQL とタイムゾーン - Yet Another Hackadelic
Time::Piece::MySQL とタイムゾーン (訂正あり) - Yet Another Hackadelic
Unix timeにタイムゾーンの概念を持ち込むのは気持ち悪いです。
得られたUnix timeに対してタイムゾーンに対応する目的で数値を加算減算してはいけないと思います。
何故ならUnix timeは「UTCにおける1970/1/1からの経過秒数」だからです。
タイムゾーンがJSTになっていて、NOW()はJSTにおける時刻を返しているという前提で話を進めますが、
(当然のことながら、UNIX_TIMESTAMP()はUTCにおける時間を返している)
何が話をややこしくしているかというと、
$ perl -MTime::Piece -MTime::Piece::MySQL -e 'my $t = Time::Piece->from_mysql_datetime("2010-11-05 12:12:47"); warn $t;' Fri Nov 5 12:12:47 2010 at -e line 1.これは正しいんですけど、
この出発点から既に正しくないんです。いや、一見正しいように見えるんですけど。
これ実は「Fri Nov 5 12:12:47 2010 UTC」が出力されています。
タイムゾーン情報が無いのでTime::Pieceは渡されたものが「Fri Nov 5 12:12:47 2010 UTC」だと思っています。
が、実は
のでepochが9時間分ずれた(ように見える)値を返してきます。
この後は連鎖的に話がおかしくなってるだけなので割愛します。
普段Time::Piece使ってないので良く分からないんですが、軽くソースを見た感じc_islocalというのが$ENV{TZ}を見るためのフラグっぽいので、
% env TZ=JST-9 perl -MTime::Piece -MTime::Piece::MySQL -e 'my $t = Time::Piece->from_mysql_datetime("2010-11-05 12:12:47"); $t->[10] = 1; warn $t->epoch;' 1288926767 at -e line 1.
とすると"2010-11-05 12:12:47"をJSTだと思ってくれて、正しくUnix timeを出してくれます。
もちろんTZを変えればちゃんとUnix timeも変わってくれます。
($t->[10]とか$t->tzoffsetとかを出力してみると何となくわかるかも)
ただ、文字列変換(warn $t)したときにTZに応じて変わってくれないっぽいので、Time::Pieceはイケテないライブラリだと思います。
(Time::Pieceを良く分かってないので、もしかしたらちゃんとタイムゾーンを扱う方法があるのかも知れません)
なお個人的には、時間を扱うライブラリはparse時にタイムゾーンを指定できるようしておいて欲しいなーと思います。その方が混乱が少ないかと。
それからタイムゾーン周りはperlのUNICODEの扱い(基本的に全てutf8 flaggedで扱って、出力するときにencodeする的な話)と同様に、基本的に全てUTCでやり取りして、後に出力する時だけ適切なタイムゾーンを設定して出すようにすれば、色々な悩みは無くなるんではないかなーと思います。
id:kits perl -MTime::Piece -le '$t=Time::Piece->strptime("2010-11-05 12:12:47 +0900","%Y-%m-%d %H:%M:%S %z"); print $t->epoch' のようにすればいいように思う。それ試してダメだったのにそんなバカな…と思ってもう一度試してみた。Time::Pieceアップデートしたら確かに↑の通りだった。
% perl-version Time::Piece Time::Piece = 1.15 % perl -MTime::Piece -le '$t=Time::Piece->strptime("2010-11-05 12:12:47 +0900","%Y-%m-%d %H:%M:%S %z"); print $t->epoch' Error parsing time at /usr/lib/perl5/5.10/i686-cygwin/Time/Piece.pm line 470. % perl-version Time::Piece Time::Piece = 1.20 % perl -MTime::Piece -le '$t=Time::Piece->strptime("2010-11-05 12:12:47 +0900","%Y-%m-%d %H:%M:%S %z"); print $t->epoch' 1288926767