Time::Piece とタイムゾーンの甘い罠
use Time::Piece; use Time::Seconds; use POSIX qw(strftime); sub print_tp { my $tp = shift; printf("date : %s\ntzoffset: %d\nepoch : %d\nstrftime: %s\n\n", $tp->strftime('%Y-%m-%d %T'), $tp->tzoffset, $tp->epoch, strftime('%Y-%m-%d %T', localtime($tp->epoch)), ); } print "Time::Piece ",$Time::Piece::VERSION,"\n"; my $tp; $tp = Time::Piece->strptime("2011-01-01", "%Y-%m-%d"); print_tp($tp); $tp = localtime( Time::Piece->strptime("2011-01-01", "%Y-%m-%d") ); print_tp($tp); $tp = $tp + ONE_DAY; print_tp($tp);
まずこれ。
$tp = Time::Piece->strptime("2011-01-01", "%Y-%m-%d"); print_tp($tp); ↓ date : 2011-01-01 00:00:00 tzoffset: 0 epoch : 1293840000 strftime: 2011-01-01 09:00:00
うっかり何も考えずに strptime を使うと、環境変数TZがどうであれ、得られたインスタンスのタイムゾーンはGMT(UTC)になります。このことを忘れて $tp->epoch の値を使うと、日本だと9時間ずれた結果になってしまいます。例えば、日次の統計処理で数値がどうも合わないとか。。。
これは、
$tp = localtime( Time::Piece->strptime("2011-01-01", "%Y-%m-%d") ); print_tp($tp); ↓ date : 2011-01-01 00:00:00 tzoffset: 32400 epoch : 1293807600 strftime: 2011-01-01 00:00:00
のようにlocaltime()したインスタンスを使えばOKです。
また、このように、
$tp = $tp + ONE_DAY; print_tp($tp); ↓ date : 2011-01-02 00:00:00 tzoffset: 32400 epoch : 1293894000 strftime: 2011-01-02 00:00:00
元のインスタンスにタイムゾーンがついていれば、それを演算をしてもタイムゾーンの情報はちゃんと受け継がれます。
ここまででひとハマり。
次。
同じスクリプトなのに、ちゃんと localtime() しているのに、僕は全然悪くないはずなのに、なぜか環境によって期待したようにタイムゾーンがつかないケースがあったのです。
こんな感じに。
$tp = localtime( Time::Piece->strptime("2011-01-01", "%Y-%m-%d") ); print_tp($tp); ↓ date : 2011-01-01 00:00:00 tzoffset: 0 epoch : 1293840000 strftime: 2011-01-01 09:00:00
よくよくみると、Time::Piece のバージョンが異なり、よくよくよくよくみると、Changesにこんな記述が。。。
1.16 - Implement %z for the internal implementation of strptime(). Unfortunately this doesn't get picked up everywhere, so there are no tests for it (yet - patches welcome).
Time::Piece 1.15を含みそれより古いバージョンを使ってる場合は、窓から投げ捨てて早急に新しいバージョンを入れましょう!!
1.15_01もダメかは確認してないのですが、Perl本体に同梱されるバージョンは結構危ういので気をつけてね。
$ corelist -a Time::Piece Time::Piece was first released with perl v5.9.5 v5.9.5 1.11_02 v5.10.0 1.12 v5.10.1 1.15 v5.11.0 1.15 v5.11.1 1.15 v5.11.2 1.15 v5.11.3 1.15 v5.11.4 1.15 v5.11.5 1.15 v5.12.0 1.15_01 v5.12.1 1.15_01 v5.12.2 1.15_01 v5.13.0 1.15_01 v5.13.1 1.15_01 v5.13.2 1.15_01 v5.13.3 1.20_01 v5.13.4 1.20_01 v5.13.5 1.20_01 v5.13.6 1.20_01 v5.13.7 1.20_01