概要
PHPで、以下のようにktime関数とdate関数を使用すると、実行のタイミングによって 求める時間とずれたUnix タイムスタンプ値になることがあります。 ここではその現象を確認するためのテストプログラムの紹介です。
テストするのは以下のコードになります。
mmktime(date('H'), date('i'), date('s'), date('m'), date('d'), date('Y'));
確認用プログラム
テストに使用したプログラムです。 テストプログラムは2つのファイルに分かれています。 実行すると、1分以内に実行が完了しますが、 求める時間とずれたUnix タイムスタンプ値になるかどうかは実行するマシンに依存します。
- test.php
- mktime_test.php
実行は以下のようにtest.phpを実行します。
$ php -f test.php実行すると、test.phpで指定している$processMaxの数だけプロセスが生成され、 テスト開始までの待ち時間(秒)を含んだ開始メッセージを出力します。
start mktime test 3 now sec: 18 s wait: 41.5 s start mktime test 6 now sec: 18 s wait: 41.5 s start mktime test 1 now sec: 18 s wait: 41.5 s start mktime test 5 now sec: 18 s wait: 41.5 s ・・・
待ち時間が過ぎてテスト開始が開始されると、各プロセスが指定回数だけ問題のコードを実行します。 現在日時より過去の値が得られた場合、エラーとして現在日時と誤った日時を表示します。
mktime test: 84 now: 2012-11-17 17:59:59(1353142799) err: 2012-11-17 17:00:00(1353139200) mktime test: 25 now: 2012-11-17 17:59:59(1353142799) err: 2012-11-17 17:00:00(1353139200)
test.php
<?php $processMax = 100; $loopMax = 1000; $taskNumber = -1; for ($i = 0; $i < $processMax; $i ++) { $pid = pcntl_fork(); if ($pid == 0) { $taskNumber = $i; break; } else if ($pid == -1) { die('failure fork'); } } if ($pid == 0) { $cmd = sprintf("/usr/bin/php -f mktime_test.php %d %d &", $loopMax, $taskNumber); system($cmd); }
mktime_test.php
<?php class MktimeTest { const US = 1000000; const LOG = 'mktime_test.log'; private $taskNumber; public function test($max, $taskNumber) { $this->taskNumber = $taskNumber; $this->wait(); for ($i = 0; $i < $max; $i ++) { $this->test_sec(); } printf("end mktime test: %3d\n", $this->taskNumber); } public function wait() { $sec = date('s'); $waitSec = (60 - $sec - 1) * self::US + self::US*0.5; if ($waitSec == 0) { $waitSec = 59; } else if ($waitSec < 0) { die('leap second ?'); } printf("start mktime test %3d now sec: %d s wait: %0.1f s\n", $this->taskNumber, $sec, $waitSec/self::US); usleep($waitSec); } public function test_sec() { $nowTs = time(); $testTs = mktime(date('H'), date('i'), date('s'), date('m'), date('d'), date('Y')); if ($nowTs > $testTs) { $msg = sprintf("mktime test: %3d now: %s(%d) err: %s(%d)\n", $this->taskNumber, date('Y-m-d H:i:s', $nowTs), $nowTs, date('Y-m-d H:i:s', $testTs), $testTs); print($msg); file_put_contents(self::LOG, $msg, FILE_APPEND | LOCK_EX); } } } if ($argc != 2 && $argc != 3) { die("usage:\n php -f $argv[0] [count number] [number]\n\n"); } $loopCount = $argv[1]; if ($argc == 3) { $taskNumber = $argv[2]; } else { $taskNumber = 0; } $t = new MktimeTest(); $t->test($loopCount, $taskNumber);