業務系統中常需計算「N 個工作日後的日期」(如訂單預計送達、工單 SLA 截止、薪資發放日),需自動跳過週六、週日。本文提供兩種實現:基礎循環法與封裝函數法,並討論擴展至節假日日曆的方案。
<?php
$now = time(); // 或指定起始日期:strtotime('2024-01-08')
$day = 3600 * 24; // 一天秒數
$total = 12; // 預估循環上限(含周末緩衝)
$days = array();
for ($i = 2; $i < $total; $i++) { // 從後天開始(i=2)
$timer = $now + $day * $i;
// date('N') ISO-8601 週幾:1=週一 ... 7=週日
$num = date('N', $timer) - 2; // 轉換:-1=週一, 0=週二, ..., 3=週五, 4=週六, 5=週日
if ($num >= -1 && $num <= 3) { // 週一至週五
if (count($days) >= 10) break; // 獲取足夠數量後跳出
$days[] = date('Y-m-d', $timer);
$total += 1; // 擴展上限,補償被跳過的周末
} else {
$total = ($total == 12) ? $total + 1 : $total; // 首次遇到周末擴展上限
}
}
// 輸出
$i = 1;
foreach ($days as $day) {
echo "$i===> $day\n";
$i++;
}
?>
date('N') 返回 1-7 (週一-週日),減 2 後:-1=週一, 0=週二, 1=週三, 2=週四, 3=週五, 4=週六, 5=週日。$num >= -1 && $num <= 3 精確篩選工作日。$total 動態擴展機制:每跳過一個周末,循環上限 +1,確保最終能湊齊所需工作日數量。<?php
/**
* 獲取未來 N 個工作日日期數組
* @param string $date 起始日期,格式 'Y-m-d',默認今天
* @param int $count 需要獲取的工作日數量,默認 10
* @return array 日期字符串數組 ['Y-m-d', ...]
*/
function get_working_days($date = '', $count = 10) {
$now = empty($date) ? time() : strtotime($date);
$days = array();
$i = 1; // 從明天開始 (i=1),或根據業務調整為 0(今天) / 2(後天)
while (count($days) < $count) {
$timer = $now + 3600 * 24 * $i;
$weekday = date('N', $timer); // 1-7
if ($weekday >= 1 && $weekday <= 5) { // 週一至週五
$days[] = date('Y-m-d', $timer);
}
$i++;
}
return $days;
}
// 使用示例
print_r(get_working_days()); // 未來 10 個工作日
print_r(get_working_days('2024-12-20', 5)); // 從指定日期起未來 5 個工作日
while 循環自然結束,無需 break。僅跳過周末不足以應對中國法定節假日(春節、國慶等)及調休工作日。企業級方案需引入節假日日曆數據源:
// 配置文件 holidays.php
return [
'2024-01-01' => '元旦',
'2024-02-10' => '春節',
'2024-02-11' => '春節',
// ...
// 調休上班日
'2024-02-04' => 'work', // 原周日調休上班
'2024-02-18' => 'work',
];
// 判斷邏輯
$holidays = include 'holidays.php';
$isWorkday = ($weekday >= 1 && $weekday <= 5) && (!isset($holidays[$date]) || $holidays[$date] === 'work');
function is_workday($date) {
static $cache = [];
if (isset($cache[$date])) return $cache[$date];
// 調用 API: https://api.example.com/holiday?date=$date&key=YOUR_KEY
// 返回: {'is_workday': true/false, 'holiday_name': ''}
// 這裡用模擬
$result = @file_get_contents("https://api.example.com/holiday?date=$date");
$data = json_decode($result, true);
$cache[$date] = $data['is_workday'] ?? (date('N', strtotime($date)) <= 5);
return $cache[$date];
}
// 系統啟動/定時任務預計算全年工作日
$workdays_2024 = [];
for ($d = strtotime('2024-01-01'); $d <= strtotime('2024-12-31'); $d += 86400) {
$date = date('Y-m-d', $d);
if (is_workday($date)) $workdays_2024[] = $date;
}
// 運行時直接數組切片,O(1) 獲取
function get_future_workdays($start_date, $count, $workdays_array) {
$idx = array_search($start_date, $workdays_array);
if ($idx === false) $idx = 0; // 兜底
return array_slice($workdays_array, $idx + 1, $count);
}
| 場景 | 方法一 | 方法二 | 預計算法 |
|---|---|---|---|
| 單次調用 10 天 | ~0.05ms | ~0.03ms | ~0.001ms |
| 高併發 (1000 QPS) | 可接受 | 推薦 | 極佳 |
| 跨年計算 | 支持 | 支持 | 需預計算次年 |
| 節假日準確性 | 無 | 需接入數據源 | 需預計算數據源 |
date_default_timezone_set('Asia/Shanghai')),避免跨天誤差。