概要
PHPで、以下のようなmktime関数とdate関数を使用すると、実行のタイミングによって 求める時間とずれたUnix タイムスタンプ値になることがあります。 このページはその実験と結果のメモです。
$t = mktime(0, 0, 0, date('m'), date('d'), date('Y')); #ある仕事で見たコード
注. 上記のコードは、以下のようにdateとstrtotime関数を使った方がシンプルになります。
$t = strtotime(date('Y-m-d'));参考 日時処理の基本的な使い方のまとめ
実験ではテスト時間の短縮のため、以下をコードを使用します。
mktime(date('H'), date('i'), date('s'), date('m'), date('d'), date('Y')); #このコードも仕事で見たことがあります。
実験に使用したプログラムは、上記の問題あるコードを、複数プロセスで指定回数実行するものです。 求める間違った結果が得られれば、それを出力します。
・mktime関数、date関数の組み合わせテストプログラム
結果
テストプログラムを実行すると、以下のような結果になりました。
注意. 実行するマシンによって間違った結果が起こらない可能性があります。 その場合はテストプログラムのパラメータを変更して負荷が大きくなるようにします。
$ date 2012年 11月 17日 土曜日 17:59:14 JST $ php -f test.php 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 97 now sec: 19 s wait: 40.5 s start mktime test 99 now sec: 19 s wait: 40.5 s end mktime test: 74 end mktime test: 79 end mktime test: 76 end mktime test: 71 end mktime test: 78 end mktime test: 80 end mktime test: 87 end mktime test: 83 mktime test: 98 now: 2012-11-17 17:59:59(1353142799) err: 2012-11-17 17:59:00(1353142740) mktime test: 12 now: 2012-11-17 17:59:59(1353142799) err: 2012-11-17 17:59:00(1353142740) mktime test: 18 now: 2012-11-17 17:59:59(1353142799) err: 2012-11-17 17:00:00(1353139200) mktime test: 84 now: 2012-11-17 17:59:59(1353142799) err: 2012-11-17 17:00:00(1353139200) end mktime test: 97 mktime test: 92 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) end mktime test: 93 mktime test: 1 now: 2012-11-17 17:59:59(1353142799) err: 2012-11-17 17:00:00(1353139200) end mktime test: 3 end mktime test: 77 ・・・ ・・・ end mktime test: 63 end mktime test: 65
この結果では、以下のように2種類の誤った結果があります。
mktime test: 98 now: 2012-11-17 17:59:59(1353142799) err: 2012-11-17 17:59:00(1353142740) mktime test: 12 now: 2012-11-17 17:59:59(1353142799) err: 2012-11-17 17:59:00(1353142740) mktime test: 18 now: 2012-11-17 17:59:59(1353142799) err: 2012-11-17 17:00:00(1353139200) mktime test: 84 now: 2012-11-17 17:59:59(1353142799) err: 2012-11-17 17:00:00(1353139200) mktime test: 92 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) mktime test: 1 now: 2012-11-17 17:59:59(1353142799) err: 2012-11-17 17:00:00(1353139200)
1つ目は、err: 2012-11-17 17:59:00(1353142740)です。
mktime(date('H'), date('i'), date('s'), date('m'), date('d'), date('Y'));は以下のような順番で実行されます。
- date('H')
- date('i')
- date('s')
- date('m')
- date('d')
- date('Y')
- mktime()
date('H') => 17 (2012-11-17 17:59:59) date('i') => 59 (2012-11-17 17:59:59) date('s') => 00 (2012-11-17 17:00:00) date('m') => 17 (2012-11-17 18:00:00) date('d') => 11 (2012-11-17 18:00:00) date('Y') => 2012 (2012-11-17 18:00:00)
2つ目は err: 2012-11-17 17:00:00(1353139200)です。 これはdate('H')とdate('i')の間で秒が進んだためです。
date('H') => 17 (2012-11-17 17:59:59) date('i') => 00 (2012-11-17 18:00:00) date('s') => 00 (2012-11-17 18:00:00) date('m') => 17 (2012-11-17 18:00:00) date('d') => 11 (2012-11-17 18:00:00) date('Y') => 2012 (2012-11-17 18:00:00)原理的に12月31日 → 1月1日の場合、最大1年ずれる可能性があります。
date('H') => 00 (2012-12-31 23:59:59) date('i') => 59 (2012-12-31 23:59:59) date('s') => 59 (2012-12-31 23:59:59) date('m') => 12 (2012-12-31 23:59:59) date('d') => 31 (2012-12-31 23:59:59) date('Y') => 2013 (2013-01-01 00:00:00)アクセスが多く、負荷の大きいサーバーの場合、このようなことが起きやすいので 未来の日時があったり、日時関係の集計結果にずれが多い場合、このコードが紛れ込んでいるかもしれません。