師匠の散歩

Perlで遊ぼう

Perlの関数である timelocal()とlocaltime()について、CPANの解説を参考に再考察を行い、実際に扱えるサーバーのPerlバージョン違いでの挙動を調べた。引数としての月日は3月1日のみであり、該当年前後での詳細な挙動調査は行っていない。つまり、2037年3月1日は動作したと記述してある場合、2037年3月2日でも問題ないかどうかまでは確認していないことを報告しておく。

timelocal関数

これまでの通説

これまで、timelocalの説明はほぼ下記内容でしか見たことがなかった。しかし、従来の表現は普遍的ではないことがわかった。特に、1970年以前や2037年以降の日付を扱う際には、Perlのバージョン、設置するサーバーによって思った解が得られない場合があるので要注意だ。

CPANの解説

最新の仕様の説明として、CPAN Time-Local 1.2000を選定した。このページを自己流で訳し、まとめると以下のようになった。

基本
値の制限

実際のサーバーの挙動

師匠が扱えるCGI動作が可能なサーバーのPerl、PCで動作するPerlでの動作を検証し、従来での説明でtimelocal関数の動作説明ができるのか、CPANの解説は反映されているのか、普遍性はあるのかなどを確認した。

PC(WindowsXP)
Active Perl ver 5.8.9
Active Perl ver 5.12.3
FreeBSD
Perl 5.5.3
Perl 5.10.1

Perl ver 5.5.3

とあるCGIが使えるサーバー(freebsd)のPerl ver 5.5.3での実行結果から以下のことがわかった。

timelocal関数
  1. 引数($year)が-30から37のとき、timelocalとlocaltimeは逆関数の関係にあった
  2. 引数($year)が 70から137のとき、timelocalとlocaltimeは逆関数の関係にあった
  3. 引数($year)が 4桁のとき、timelocalとlocaltimeは逆関数の関係にはなかった
localtime関数
  1. 西暦1902年から2037年のエポック秒で正しい解を返した

Active Perl ver 5.8.9

WindowsXPにインストールしたActivePerl ver 5.8.9での実行結果は次のようになった。

timelocal関数
  1. 引数($year)が0から36のとき、timelocalとlocaltimeは逆関数の関係にあった
  2. 引数が70から136のとき、timelocalとlocaltimeは逆関数の関係にあった
  3. 引数が4桁のときは未確認である
localtime関数
  1. 西暦1970年から2037年のエポック秒で正しい解を返した

Perl ver 5.10.1

とあるfreebsd 64intサーバーのPerl ver 5.10.1での実行結果から以下のことがわかった。

timelocal関数
  1. 引数($year)が、0から37のとき、timelocalとlocaltimeは逆関数の関係にあった
  2. 引数($year)が 38〜61のとき、1902年から1924年に対応した
  3. 引数($year)が62から137のとき、timelocalとlocaltimeは逆関数の関係にあった
  4. 引数($year)が西暦1902から2037のとき、timelocalとlocaltimeは逆関数の関係にあった
localtime関数
  1. 西暦1902〜2037年のエポック秒で正しい解を返した

Active Perl ver 5.12.3

WindowsXPにインストールしたActivePerl ver 5.12.3での実行結果は次のようになった。

timelocal関数
  1. 引数($year)が-1900以下=(紀元前-1900)の場合、timelocalとlocaltimeは逆関数の関係になかった
  2. 引数($year)が 0から61のとき、timelocalとlocaltimeは逆関数の関係にあった
  3. 引数($year)が -1899から-1のとき、および62から999のとき、timelocalとlocaltimeは逆関数の関係にあった
  4. 引数($year)が1000以上のとき、timelocalとlocaltimeは逆関数の関係にあった
localtime関数
  1. 確認した範囲のエポック秒で正しい解を返した

まとめ

従来の (西暦年-1900)を引数とするtimelocal関数の説明に対し、西暦1970〜2036年は4つのシステムで共通して動作した。

それ以外の引数の場合の動作は、システムによって異なる結果となった。

Perl 5.10 サーバーでの使用方法

とあるCGI利用可能な無料HPサイトで2011年に確認した内容を述べる。

  1. 西暦1902/1/1〜2036/12/31でのみ対応とする
  2. これ以前・以降の日付を扱いたい場合は、timelocal/localtimeとは異なる関数を用いる
  3. timelocalの引数は(西暦年4桁)として timelocal(...,$year)としてエポック秒を求める $year-1900とはしない
  4. localtime(エポック秒)から、年月日=($year+1900)/($month+1)/day を求める
use strict;
my ($dd,$mm,$yy) = (1,2,2011);
my $epock = newTimeLocal($dd,$mm.$yy);
newLocalTime($epock);
exit;
sub newTimeLocal {
  my ($dd,$mm,$yy) = @_;
  if ($yy<1962 || $yy>2036){  print "error";  return -1;  }
  my $epock = timelocal(0,0,0,$dd,$mm-1,$yy); # エポック秒を求める
  print " timelical (0,0,0,$dd,".($mm-1).",$yy) = $epock";
  return;
}
sub newLocalTime {
  my $epock = shift;
  my ($dd,$mm,$yy) = (localtime($epock))[3..5];
  $yy +=1900;
  $mm ++;
  print "年月日 = $yy/$mm/$dd";              # 年月日を戻す
  return;
}

Active Perl 5.12 での使用方法

  1. 西暦1899年以前は、($year-1900)を引数にして timelocalでエポック秒を求める
  2. 紀元前では他の関数でエポック秒を求める(timelocalを使わない関数を用いる)
  3. 西暦1900〜1962年は、($year)を引数にしてtimelocalでエポック秒を求める
  4. 西暦1962年以降は、($year-1900)または($year)を引数として timelocalでエポック秒を求める
  5. localtime(エポック秒)から、年月日=($year+1900)/($month+1)/day を求める
use strict;
my ($dd,$mm,$yy) = (1,2,2011);
if ($yy<1900){
 $yy -=1900;
}
my $epock = timelocal(0,0,0,$dd,$mm-1,$yy); # エポック秒を求める
&printtime ($epock);

sub printtime {
 my $epock = shift;
 my ($dd,$mm,$yy) = (localtime($epock))[3..5];
 $yy +=1900;
 $mm ++;
 print "年月日 = $yy/$mm/$dd";              # 年月日を戻す
 return;
}

結論

ネットでよく紹介されている次の文章は、たいていのサーバーにおいて、1970〜2036では問題なく動作するようだ。これ以外の西暦年を扱う場合は、timelocal/localtimeの仕様をよく理解して使う必要がある。より普遍性を望む人は、エポック秒ではなく、ユリウス日のような他のデータ形式を用いることを師匠はおすすめする。

注釈
ここまで年または西暦年と記述したものは、グレゴリウス暦のことを示す。
CPANについて
timelocal関数についてCPANに記述されているものが、ActivePerlで全て実現しているわけではない
CPANでは肝心なところは「サーバーによる」と逃げており、一意的な表現はしていない

追記(2011/4/3)

なお、師匠が使用しているパソコン(WindowsXP)の内部時計を進ませ擬似的に未来はどうなのか確認しようとしたところ、内部時計を2038年以降にするとAnHTTPDがエラーを発生したためCGIの動作は確認できなかった。他にもエラーが発生したソフトがあるため、内部 time_t が2037年問題を解決していないことが原因ではないかと推測するが、本当のところどうなのかは良く分からない。さらに、freebsdマシンで、師匠がtelnet接続して内部時計を自由に変更できる鯖は持ち合わせていないため、こちらでの動作は確認できなかった。


Topに戻る // 戻る
Copyright(C) Grandmaster since 2010最終更新:2015/5/15