-
Time::Piece->strptime()
で返されるオブジェクトは GMT -
Time::Piece->strptime()
では1899年以前の日付は解釈できない -
gmtime->strftime('%Z')
は,オブジェクトが GMT であるにもかかわらず,localtime
のタイムゾーン名を返す (%z
も同様) ⇒ 追記 そのうち治りそう (2015-04-10) -
Time::Piece
のオブジェクトは,自身がlocaltime
かgmtime
であるかのフラグを持っている。だがタイムゾーンはもっていない。(#tzoffset
は呼び出される度に算出する)
Time::Piece->strptime()
で返される値は GMT
use Time::Piece;
Time::Piece->strptime('2013-11-03 12:34:56', '%Y-%m-%d %H:%M:%S')->strftime;
# => 日, 03 11月 2013 12:34:56 UTC
これは %z
等でタイムゾーンを解釈させても,かわらない。
Time::Piece->strptime('2013-11-03 +0900', '%Y-%m-%d %z')->strftime;
# => 土, 02 11月 2013 15:00:00 UTC
Time::Piece->strptime()
では1899年以前の日付は解釈できない
Time::Piece->strptime('1868-10-23', '%Y-%m-%d');
# => Error parsing time at ...
どうしてこうなるのかは Time::Piece の Parse.xs の _strptime() でも読んでください。
なお Time::Piece
オブジェクトが1899年以前を表象できないわけではない。
use Time::Piece;
use Time::Local;
$t = Time::Local::timelocal(0,0,0, 23, 10-1, 1868);
# => -3193291139
localtime($t)->strftime;
# => 金, 23 10月 1868 00:00:00 JST
Time::Local
は Time::Local
で西暦999年以前を設定できないという点もあるのだけれど。そのへんは perldoc でもみてください。
gmtime->strftime('%Z')
は,オブジェクトが GMT であるにもかかわらず,localtime
のタイムゾーン名を返す (%z
も同様)
localtime->strftime('%z %Z');
# => +0900 JST
gmtime->strftime('%z %Z');
# => +0900 JST
これはバグだと思うので,一応 Pull Request はだしてある。
追記 (2015-04-10) 1.29_03 で取り込まれたので 1.30 以降では治るかな? https://github.com/rjbs/Time-Piece/commit/ecc37f3ec3b03a917076f474d3c4c64640384b01
ちなみに C の strftime()
はきちんと localtime
と gmtime
で %z
の出力をだしわける。
#include <stdio.h>
#include <time.h>
int main(int argc, char *argv[])
{
char buf[1024];
time_t t = time(NULL);
strftime(buf, 1024, "%z; %Z; %c\n", localtime(&t));
puts(buf); /* => +0900; JST; Sun Nov 3 17:42:42 2013 */
strftime(buf, 1024, "%z; %Z; %c\n", gmtime(&t));
puts(buf); /* => +0000; GMT; Sun Nov 3 08:42:42 2013 */
return 0;
}
Time::Piece#strftime
の出力がおかしい原因としては Perl のソースの Util.c
で定義されている init_tm()
が localtime
で得られる結果をコピーしており,Time::Piece#strftime
ではその init_tm()
を使って作成した構造体を C の strftime()
にわたしているからである。
なので Pull Request はわりと adhoc な解決策になっている。
Time::Piece
のオブジェクトは,自身が localtime
か gmtime
であるかのフラグを持っている。だがタイムゾーンはもっていない。(#tzoffset
は呼び出される度に算出する)
フラグ (c_islocal
) については以下の通り。
use Time::Piece;
use Data::Dumper;
say Data::Dumper->new([ scalar localtime ])
->Indent(1)->Terse(1)->Useqq(1)->Dump();
# => bless( [
# 33, # c_sec
# 9, # c_min
# 18, # c_hour
# 3, # c_mday
# 10, # c_mon
# 113, # c_year
# 0, # c_wday
# 306, # c_yday
# 0, # c_isdst
# "1383469773", # c_epoch
# 1 # c_islocal
# ], 'Time::Piece' )
いくつかの Time::Piece
のメソッドは,このフラグをみて挙動をかえている。
Time::Piece::c_islocal
定数を使って,このフラグにアクセスすることができる (が,推奨されない,と思う)。
use Time::Piece;
my $lt = localtime;
$lt->[Time::Piece::c_islocal]; # => 1
my $gt = gmtime;
$gt->[Time::Piece::c_islocal]; # => 0
ちなみに,BSD や glibc の struct tm
は,islocal
のようなフラグのかわりに,UTC
からのオフセットとタイムゾーンの文字列を保持している。Time::Piece
もそのようになっていたら便利だったのに……
/* time.h from GNU C Library */
/* ...... snip snip snip ...... */
struct tm
{
int tm_sec; /* Seconds. [0-60] (1 leap second) */
int tm_min; /* Minutes. [0-59] */
int tm_hour; /* Hours. [0-23] */
int tm_mday; /* Day. [1-31] */
int tm_mon; /* Month. [0-11] */
int tm_year; /* Year - 1900. */
int tm_wday; /* Day of week. [0-6] */
int tm_yday; /* Days in year.[0-365] */
int tm_isdst; /* DST. [-1/0/1]*/
#ifdef __USE_BSD
long int tm_gmtoff; /* Seconds east of UTC. */
__const char *tm_zone; /* Timezone abbreviation. */
#else
long int __tm_gmtoff; /* Seconds east of UTC. */
__const char *__tm_zone; /* Timezone abbreviation. */
#endif
};
/* ...... snip snip snip ...... */
Time::Piece
では,#tzoffset
(GMT
の場合は常に 0) の呼び出しごとに GMT
との差分を算出している。つまり,Time::Piece
オブジェクトが生成された時点の TZ
ではなく,#tzoffset
を呼び出した時点での TZ
をもとに差分を算出するので,(状況によっては) 注意が必要である。
タイムゾーン,オフセットを保持していないので,(複数箇所のタイムゾーンの時刻オブジェクトを利用するケースにおいて) そのようなものを内包する日時コンテナオブジェクトとして扱ったりシリアライズに使ったりするには不適当である。
(もちろん,実質 local time しか扱わないアプリケーションにおいては,十分な要件をみたしている)