きっかけは画像を出力するPHPを作ってたときに、Last-Modifiedを作る必要があって。
で、
http://www.studyinghttp.net/header#Last-Modified
を読んでたらHTTP-Dateというのが出てきて、
http://www.studyinghttp.net/header#HTTP-Date
を読んでたら"GMT表記"が必要らしい。
今まであまり気にしてきませんでしたが、Last-Modified や If-Modified-Since は特に画像などをPHPでブリッジして出力させるとき、パケット量を抑える為に必須です。である以上、それなりにきっちりしておかないと、せっかく実装してもGMT関連の認識ズレで304が返せない・・・ってなことにもなりかねません。
で、基本。
DOS>php -r "echo mktime(0, 0, 0, 1, 1, 1970);" Warning: mktime(): Windows does not support negative values for this function in Command line code on line 1 -1 DOS>php -r "echo mktime(9, 0, 0, 1, 1, 1970);" 0 DOS>php -r "echo gmmktime(9, 0, 0, 1, 1, 1970);" 32400
ええっと、つまり、mktimeは「その地域での時刻表記→GMTでは何秒経過しているか」を算出。
で、gmmktimeは「今いる場所をGMTとして、今の時刻表記をそのままGMTでは何秒経過しているか」を算出。
だから、
32400 = 3600 * 9
つまり9時間分、ずれているわけ。
DOS>php -r "echo strftime('%a, %d %b %Y %H:%M:%S', mktime(9, 0, 0, 1, 1,1970));" Thu, 01 Jan 1970 09:00:00
→これは、mktime()は・・・0を返す。つまり、日本時間で9時というのは、GMTでは0
秒ということ。
で、strftimeの引数は、GMTでのタイムスタンプ。つまり、0秒→GMTでの0秒→当然、
日本時間では9時。
DOS>php -r "echo gmstrftime('%a, %d %b %Y %H:%M:%S', mktime(9, 0, 0,1, 1, 1970));" Thu, 01 Jan 1970 00:00:00
→gmstrftimeの引数も当然GMTでのタイムスタンプ。ただし、表記した結果はGMTでの
表記になる。
つまり、0秒→GMTでの0秒→GMTでの表記になるから、当然、00:00:00.
DOS>php -r "echo strftime('%a, %d %b %Y %H:%M:%S', gmmktime(9, 0, 0,1, 1, 1970));" Thu, 01 Jan 1970 18:00:00
→これは間違えたパターン。gmmktimeで返されるのは、引数をGMTとみなしたときの秒数。つまり、9*3600が返される。
で、これをそのままGMTでの9*3600と考えたので、strftimeは日本時間に直すときにさらに9時間足すため、18時になる。
DOS>php -r "echo gmstrftime('%a, %d %b %Y %H:%M:%S', gmmktime(9, 0, 0, 1, 1, 1970));" Thu, 01 Jan 1970 09:00:00
→これは、IN/OUT両方ともGMTでそろえた場合。引数にはGMTでの時間を渡し、GMTでの秒が返され、gmstrftimeではGMTでの秒数として受け取り、GMTでの表記で出力している。
こう考えると、引数や返り値の"timestamp"はあくまでも"GMT「としてみなされる」"ことが暗黙の前提と考えてよいような気がする。
例えば、2007-04-25 18:15:XX付近で以下のコードを実行してみる。
DOS>php -r "echo gmmktime(18, 15, 0, 4, 25, 2007);" 1177524900 DOS>php -r "echo time();" 1177492531
これ、コマンドラインで実行する時差を補正すれば、ちょうど9時間ずれている。逆に、mktime()だとちょうど同じ。
ようするに、time()は、現在のロケールと同時点でのGMTの秒数を返す。
つまり、ロケールに基づく時差をZとし、現在ロケールの時刻表現をargとすれば、
mktime = arg - Z = time() gmmktime = arg = time() + Z
よって、以下になる。
DOS>php -r "echo gmmktime(18, 15, 0, 4, 25, 2007) - mktime(18, 15, 0, 4, 25, 2007);" 32400(=9 * 3600) (= arg - (arg - Z))
通常のmktimeやstrftimeが、内部的にZを補正するのは・・・time()の挙動に引きずられているとみなせる。
time()自体はGMT秒数を出力するため、常に GMT となる。
strftimeを、f(ARG) = ARG + Z として定義すれば、
strftime(GMT) = GMT + Z
となり、現在ロケールでの表記が取得できる。ここで、
X(ARG) = strftime(mktime(ARG))
とすれば、
X(ARG) = mktime(ARG) + Z = ARG - Z + Z = ARG
として、最初にmktimeに渡した、現在ロケールに基づくタイムスタンプと同一の、文字表現が取得できる。
"同時点"というのがキーワードになる。
mktime, strftimeは、結局"同時点"でのGMTを返したり、引数にとる。
gmktime, gmstrftimeは、同時点補正を行わない。つまり、今いる位置がグリニッジと想定する。
逆に言えば、"同時点でのGMTでの表記がほしい!"となれば?time()はGMT時間を返す。
あとはそれをそのまま、グリニッジにいると思わせればよいので、gmstrftimeに mktime() か time() を食わせればよい。
しかしこれ、実際にHTTP Headerで使うとしたら、ブラウザ側どうして来るんだろうな・・・。
結局"同時点でのGMT"を中心に動かすわけで・・・動くんだよな・・・。
echo strftime('%a, %d %b %Y %H:%M:%S GMT', mktime(9, 0, 0, 1, 1, 1970)); (→PHP4.3.6 on Windowsだと、%T(=%H:%M:%S)が認識されなかった) Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123 Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036 Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
コメント