PHPの日付/時刻関数にmktime関数というのがあります。年月日時分秒の6引数からいわゆるunix timeを求める関数ですが、これがPHPの最近のバージョンで並外れて遅いことがあるという話題です。毎度おなじみのパターンですが、今回もかなりのレアケースです(レアケースじゃなかったら既に誰か気づいているはずです)。
mktime - 日付を Unix のタイムスタンプとして取得する
説明
int mktime ([ int $hour [, int $minute [, int $second [, int $month [, int $day [, int $year [, int $is_dst ]]]]]]] )
与えられた引数に従って UNIX のタイムスタンプを返します。 このタイムスタンプは、Unix epoch(1970年1月1日00:00:00 GMT)から 指定された時刻までの通算秒を表す長整数です。
引数は右から順に省略することができます。省略された引数は、 ローカルの日付と時刻に従って、現在の値にセットされます。
PHP: mktime - Manual
この関数がおそらく他言語の同等の関数に比べて変わっている点は、各引数の範囲が特に定まっていないことです。時刻としてありえない範囲の数値が渡された場合は、自然な拡張として解釈されます。月に0が渡されたら前年の12月の意味、秒に-60が渡されたら1分前の意味になります。
また、PHP5.0.5までは日付周りのPHP関数の内部でmktime(3)をライブラリコールしていたのですが、PHP5.1.0からはunix timeの計算を自前で実装しています。この自前実装でunix timeを負の方向に自然に拡張することで、1970-01-01 00:00:00GMT以前の日時に正式に対応しました。これまでもmktime(3)が負のunix timeを返す環境では負のunix timeに対応していたのですが、PHP5.1.0からは言語として正式に負のunix timeに対応したということです。
ところで、このPHP5.1.0で新たに実装された時刻の処理がナイーブな実装で、mktime関数に負の引数を渡すとのんびりした処理をします。小さければ小さいほどのんびり処理します。私の手元の環境で実験してみました。
<?php var_dump(mktime(9,0,-2147483647,1,1,1970));
上記PHPスクリプトで「1970年1月1日9時0分-2147483647秒JST」を計算してみます。
# time php-5.0.5 slow-mktime.php int(-2147483647) real 0m0.029s user 0m0.019s sys 0m0.010s # time php-5.2.5 slow-mktime.php int(-2147483647) real 0m2.135s user 0m2.123s sys 0m0.009s #
( ゚д゚) (つд⊂)ゴシゴシ (;゚д゚) (つд⊂)ゴシゴシゴシゴシ (;゚ Д゚)2秒…?
僕のマシンがいくら遅いといってもこれは遅すぎなのでは…。ちなみにPHP5.0.5はライブラリコールを使っており、この環境では負のunix timeを正しく返しているわけですが、PHPの自前実装版に比べると100倍くらい早いみたいですね。
別に誰も困らないとは思いますし、こんなレアケースを直すのに頑張るくらいなら他のことに時間を使った方がいいと思いますけどね。