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」だと思っています。

  • 「Fri Nov 5 12:12:47 2010 JST(=Fri Nov 5 03:12:47 2010 UTC)」を渡しているつもり

が、実は

  • 「Fri Nov 5 12:12:47 2010 UTC(=Fri Nov 5 21:12:47 2010 JST)」を渡している

ので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時にタイムゾーンを指定できるようしておいて欲しいなーと思います。その方が混乱が少ないかと。
それからタイムゾーン周りはperlUNICODEの扱い(基本的に全て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