周報系統、考勤統計、財務結算常需「第 N 週的起迄日期」。PHP date('W') 遵循 ISO-8601:週一為週首日,首週包含該年第一個週四。
<?php
/**
* 取得指定年份每週的起始(週一)與結束(週日)日期
*
* @param int $year 年份,如 2024
* @return array 索引 1~52/53,值為 ['start'=>'Y-m-d', 'end'=>'Y-m-d']
*/
function getYearWeeks(int $year): array
{
// 1. 找出該年第一個週一
$firstDayOfYear = new DateTime("$year-01-01");
$firstMonday = clone $firstDayOfYear;
$firstMonday->modify('monday this week'); // 若 1/1 是週一則不動,否則往前推到上週一
// 修正:ISO-8601 首週定義 = 包含第一個週四的那一週
// 簡化做法:若 1/1 是週五/六/日,則第一週實際從下週一開始
$dow = (int)$firstDayOfYear->format('N'); // 1=週一 ... 7=週日
if ($dow > 4) { // 週五、六、日
$firstMonday->modify('next monday');
}
// 2. 找出該年最後一個週日
$lastDayOfYear = new DateTime("$year-12-31");
$lastSunday = clone $lastDayOfYear;
$lastSunday->modify('sunday this week'); // 若 12/31 是週日則不動,否則往後推到本週日
$dowEnd = (int)$lastDayOfYear->format('N');
if ($dowEnd < 7) { // 週一~週六
$lastSunday->modify('next sunday');
}
// 3. 計算總週數
$totalWeeks = (int)$lastSunday->format('W');
if ($totalWeeks === 0) $totalWeeks = 1; // 邊界情況
// 4. 產生每週區間
$weeks = [];
for ($i = 1; $i <= $totalWeeks; $i++) {
$start = clone $firstMonday;
$start->modify("+".($i-1)." weeks");
$end = clone $start;
$end->modify('+6 days');
// 不超過該年最後一天
if ($end > $lastDayOfYear) {
$end = clone $lastDayOfYear;
}
$weeks[$i] = [
'start' => $start->format('Y-m-d'),
'end' => $end->format('Y-m-d'),
'label' => "第 {$i} 週",
];
}
return $weeks;
}
// === 使用範例 ===
$weeks2024 = getYearWeeks(2024);
echo "2024 年週曆:\n";
foreach ($weeks2024 as $num => $week) {
printf("第 %2d 週: %s ~ %s\n", $num, $week['start'], $week['end']);
}
// 取得特定週
$week18 = $weeks2024[18] ?? null;
if ($week18) {
echo "\n第 18 週: {$week18['start']} ~ {$week18['end']}";
}
2024 年週曆:
第 1 週: 2024-01-01 ~ 2024-01-07
第 2 週: 2024-01-08 ~ 2024-01-14
...
第 18 週: 2024-04-29 ~ 2024-05-05
...
第 52 週: 2024-12-23 ~ 2024-12-29
第 53 週: 2024-12-30 ~ 2024-12-31
function getWeekRangeStr(int $year, int $week): ?array
{
if ($week < 1 || $week > 53) return null;
$monday = strtotime("{$year}-W" . str_pad($week, 2, '0', STR_PAD_LEFT) . '-1');
$sunday = strtotime("+6 days", $monday);
// 驗證是否仍在該年內(跨年週處理)
$mondayYear = (int)date('Y', $monday);
if ($mondayYear !== $year && $week !== 1) return null;
return [
'start' => date('Y-m-d', $monday),
'end' => date('Y-m-d', $sunday),
];
}
// 使用
var_dump(getWeekRangeStr(2024, 18));
// ['start'=>'2024-04-29', 'end'=>'2024-05-05']
function getWeeksCustom(int $year, int $weekStart = 1): array // 1=週一, 0=週日
{
$weeks = [];
$date = new DateTime("$year-01-01");
// 找第一個符合週首日的日期
while ((int)$date->format('w') !== $weekStart) {
$date->modify('+1 day');
}
$endOfYear = new DateTime("$year-12-31");
$weekNum = 1;
while ($date <= $endOfYear) {
$start = clone $date;
$end = clone $date;
$end->modify('+6 days');
if ($end > $endOfYear) $end = clone $endOfYear;
$weeks[$weekNum++] = [
'start' => $start->format('Y-m-d'),
'end' => $end->format('Y-m-d'),
];
$date->modify('+7 days');
}
return $weeks;
}