※下書き段階で投稿してしまったので、再投稿
PHPのdate('W')が返す週番号は、第53週では'01'を返します。strftime('%W')も同様です。
<?php echo date('W', strtotime('2013-12-31')), PHP_EOL; // '01'
この仕様は、ISO規格に基づいています。
http://ja.wikipedia.org/wiki/ISO_8601#.E5.B9.B4.E3.81.A8.E9.80.B1.E3.81.A8.E6.9B.9C.E6.97.A5
以下、上記Wikipedia記事より引用
年末において以下の曜日に該当する場合、その日は当年最終週の曜日としてでは無く、翌年第1週の曜日として扱うものとされている。12月29日が月曜日の場合。 12月30日が月曜日または火曜日の場合。 12月31日が月曜日・火曜日・水曜日のいずれかの場合。
この挙動は、PHPのバグではありません。しかし、PHPアプリケーションにおいてバグを作りこみがちな挙動の1つだと思います(実際、私も2014年最初に修正したバグは、この挙動に起因するバグでした)。
以下は、上記条件の場合には、'01'ではなく'53'を返す関数です。
<php
/**
* date('W')は、以下の場合に週番号として'01'を返す。
* (1) 12月29日が月曜日の場合。
* (2) 12月30日が月曜日または火曜日の場合。
* (3) 12月31日が月曜日・火曜日・水曜日のいずれかの場合。
*
* この関数では、12/29〜31で、週番号が'01'となる場合には、'53'を返す。
*
* @param $time UNIXタイムのタイムスタンプ
* @return string
*/
function getWeekNumber($time)
{
$week_number = date('W', $time); // 週番号(01から52)
$month_and_day = date('m-d', $time); // 月-日
$day_of_the_week = date('w', $time); // 0 (日曜)から 6 (土曜)
// 処理方法(1): 仕様通りに月日と曜日で判定
if ($month_and_day === '12-29' && in_array($day_of_the_week, array(1))) {
$week_number = '53';
}
if ($month_and_day === '12-30' && in_array($day_of_the_week, array(1, 2))) {
$week_number = '53';
}
if ($month_and_day === '12-31' && in_array($day_of_the_week, array(1, 2, 3))) {
$week_number = '53';
}
// 処理方法(2): 月日+date('W')の値で判定(こちらでも実用上は問題ないかと…)
// if (in_array(date('m-d', $time), array('12-29', '12-30', '12-31')) && date('W', $time) === '01') {
// $week_number = '53';
// }
return $week_number;
}
「53」を返す処理については、仕様通りに「月日」+「曜日」で判定を行っています。ただ、この関数が必要な場合というのは、「01」が返されて困る場合に「53」を返すことだと思います。したがって、処理方法(2)でも、実用上は問題無いでしょう。
以下は、テスト付きのバージョンです。phpコマンドで実行して、結果が「ok」ならテストに合格したことになります。また、TAPに基づいたテストなので、proveコマンドでもテストできます(proveで実行するため、1行目にシバンを付けています)。
#!/usr/bin/env php <?php test(9); is(getWeekNumber(strtotime('2014-12-29')), '53', 1, '2014年12月29日(月) => 53'); is(getWeekNumber(strtotime('2014-12-30')), '53', 2, '2014年12月30日(火) => 53'); is(getWeekNumber(strtotime('2014-12-31')), '53', 3, '2014年12月31日(水) => 53'); is(getWeekNumber(strtotime('2013-12-30')), '53', 4, '2013年12月30日(月) => 53'); is(getWeekNumber(strtotime('2013-12-31')), '53', 5, '2013年12月31日(火) => 53'); is(getWeekNumber(strtotime('2012-12-31')), '53', 6, '2012年12月31日(月) => 53'); is(getWeekNumber(strtotime('2011-12-29')), '52', 7, '2011年12月29日(木) => 52'); is(getWeekNumber(strtotime('2011-12-30')), '52', 8, '2011年12月30日(金) => 52'); is(getWeekNumber(strtotime('2011-12-31')), '52', 9, '2011年12月31日(土) => 52'); /** * @param int $number_of_tests */ function test($number_of_tests = 0) { if ($number_of_tests > 0) { echo '1..', $number_of_tests, PHP_EOL; } } /** * @param $got * @param $expected * @param int $test_number * @param string $description テストの説明 * @param string $directive 「TODO」又は「SKIP」+ TODO/SKIPである理由 */ function is($got, $expected, $test_number, $description = '', $directive = '') { if ($expected === $got) { echo 'ok ', $test_number; } else { echo 'not ok ', $test_number; } if ($description !== '') { echo ' - ', $description; } if ($directive !== '') { echo ' # ' . $directive; } echo PHP_EOL; if ($expected !== $got) { echo '# got: ', $got, PHP_EOL; echo '# expected: ', $expected, PHP_EOL; } } /** * date('W')は、以下の場合に週番号として'01'を返す。 * (1) 12月29日が月曜日の場合。 * (2) 12月30日が月曜日または火曜日の場合。 * (3) 12月31日が月曜日・火曜日・水曜日のいずれかの場合。 * * この関数では、12/29〜31で、週番号が'01'となる場合には、'53'を返す。 * * @param $time UNIXタイムのタイムスタンプ * @return string */ function getWeekNumber($time) { $week_number = date('W', $time); // 週番号(01から52) $month_and_day = date('m-d', $time); // 月-日 $day_of_the_week = date('w', $time); // 0 (日曜)から 6 (土曜) // 処理方法(1): 仕様通りに月日と曜日で判定 if ($month_and_day === '12-29' && in_array($day_of_the_week, array(1))) { $week_number = '53'; } if ($month_and_day === '12-30' && in_array($day_of_the_week, array(1, 2))) { $week_number = '53'; } if ($month_and_day === '12-31' && in_array($day_of_the_week, array(1, 2, 3))) { $week_number = '53'; } //処理方法(2): 月日+date('W')の値で判定(こちらでも実用上は問題ないかと…) // if (in_array(date('m-d', $time), array('12-29', '12-30', '12-31')) && date('W', $time) === '01') { // $week_number = '53'; // } return $week_number; }