2011年11月8日火曜日

strftimeで日付がずれる

もしかしたら有名な話なのかもしれませんが、今日 スケジュールの画面のデモをphpで作っていたときにちょっとはまった。 クライアントにイメージをつかんでもらうための簡単なカレンダを phpでループさせて作っていたのだが、デモプログムの一部で、
date_default_timezone_set('Asia/Tokyo');
$base = strtotime('2011-11-01');
$days = explode(',', '日,月,火,水,木,金,土');
for ($i=0; $i<30; ++$i) {
 $date = $base+24*60*60*$i;
 $mmdd = strftime('%m/%d', $date);
 $day = $days[strftime('%w', $date)];
 echo "<br>$i: $mmdd($day)";
}

みたいに、今月の頭からループで24時間づつ増やしながら日付を表示させて、カレンダの日付の部分を表示させようとしたら、想定外の出力に!

0: 11/01(火)
1: 11/02(水)
2: 11/03(木)
3: 11/04(金)
4: 11/05(土)
5: 11/06(日)
6: 11/06(日)

7: 11/07(月)
8: 11/08(火)
9: 11/09(水)
10: 11/10(木)
: 
打ち合わせ開始まで30分しかなかったので、軽いパニックに。
誤差が蓄積されたような動きであったので、とりあえず、その場は
$base = strtotime('2011-11-01 14:00');
変更したところ、想定の出力で動き、残りの部分のコーディングに戻ることができた。

気になったので、今、「夏時間」をwikipediaで調べたら、


アメリカ合衆国(一部除く。前述のとおり2007年から次のように変更され実行されている)、カナダ(一部除く)、メキシコ(一部除く) - 3月第2日曜日午前2時〜11月第1日曜日午前2時(現地時間基準。開始日には2時が3時になり(1時59分59秒の次が3時00分00秒)、終了日は2時が再度1時(1時59分59秒の次が1時00分00秒)になるため、開始日は1日が23時間、終了日は逆に25時間になる)
と、アメリカでは11/6は25時間あることがわかった。ちなみに前のスクリプトに時間まで表示してみると、
0: 11/01 00:00(火)
1: 11/02 00:00(水)
2: 11/03 00:00(木)
3: 11/04 00:00(金)
4: 11/05 00:00(土)
5: 11/06 00:00(日)
6: 11/06 23:00(日)

7: 11/07 23:00(月)
8: 11/08 23:00(火)
9: 11/09 23:00(水)
10: 11/10 23:00(木)
11/6の00:00に24時間を足すと、11/6の23:00になっている。


ああ、すっきり!!、でもさ、夏時間のない'Asia/Tokyo'のロケールのはずじゃん。まいっか。いや、よくないな、よくない。
ここで、デジャヴが、どっかの環境で'Asia/Tokyo'が効かなくて、GMT-9って指定したことあったっけ。

 date_default_timezone_set('Etc/GMT-9');
 $base = strtotime('2011-11-01');
 $days = explode(',', '日,月,火,水,木,金,土');
 for ($i=0; $i<30; ++$i) {
  $date = $base+24*60*60*$i;
  $mmdd = strftime('%m/%d  %H:%M', $date);
  $day = $days[strftime('%w', $date)];
  echo "<br>$i: $mmdd($day)";
 }
 結果は
0: 11/01 00:00(火)
1: 11/02 00:00(水)
2: 11/03 00:00(木)
3: 11/04 00:00(金)
4: 11/05 00:00(土)
5: 11/06 00:00(日)
6: 11/07 00:00(月)

7: 11/08 00:00(火)
8: 11/09 00:00(水)
9: 11/10 00:00(木)
10: 11/11 00:00(金)
と、よしっ!きれいだ。ってなんでAsia/Tokyoと結果が違うの?
あ、そっか。日本って震災の節電の関係で夏時間設定されたんだっけ。
なんか違う。眠いからに違いない。寝よう。

よし、リフレッシュ。

ソースを少し変えてタイムゾーンを変えて流せるようにした。

echo "<pre>\n";
printDays('UCT');
printDays('Etc/GMT-9');
printDays('Asia/Tokyo');
printDays('America/New_York'); 
function printDays($timezone) {
echo "timezone:$timezone\n";
date_default_timezone_set($timezone);
$base = strtotime('2011-11-01');
for ($i=4; $i<8; ++$i) {
$date = $base+24*60*60*$i;
$mmdd = strftime('%m/%d  %H:%M', $date);
echo "$i: $mmdd\n";
}
echo "base:$base\n";
echo "now:".strftime('%Y-%m-%d %H:%M:%S')."\n\n";
}
結果はこんな感じ
timezone:UCT
4: 11/05 00:00
5: 11/06 00:00
6: 11/07 00:00
7: 11/08 00:00
base:1320105600
now:2011-11-08 23:50:43 
timezone:Etc/GMT-9
4: 11/05 00:00
5: 11/06 00:00
6: 11/07 00:00
7: 11/08 00:00
base:1320073200
now:2011-11-09 08:50:43 
timezone:Asia/Tokyo
4: 11/05 00:00
5: 11/06 00:00
6: 11/06 23:00
7: 11/07 23:00
base:1320120000
now:2011-11-08 18:50:43 
timezone:America/New_York
4: 11/05 00:00
5: 11/06 00:00
6: 11/06 23:00
7: 11/07 23:00
base:1320120000 
now:2011-11-08 18:50:43
そうそう、 日本は震災でアメリカに..そんなわけない。
使っているサーバのタイムゾーンのデータが壊れているようだ。

ローカルのphp5.3.3の環境では、timezone:Asia/Tokyoの表示は
timezone:Etc/GMT-9と同じ結果になっている。
不思議な動作をするサーバはphp5.3.1。
業務で使うサーバもほぼ同じ環境だから確認しておかなくては。