概要
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);