1. 概要
    • 結果

      概要

      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()
        しかし、ちょうど秒が59 → 00 となるようなタイミングで、問題のコードが実行されると、 date('i')の実行後、date('s')の実行前に秒が進むことがあります。このような場合 今回のような求めている時間とずれたUnix タイムスタンプ値になります。
        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)
        
        アクセスが多く、負荷の大きいサーバーの場合、このようなことが起きやすいので 未来の日時があったり、日時関係の集計結果にずれが多い場合、このコードが紛れ込んでいるかもしれません。