用戶輸入的日期(如生日、預約時間)常包含非法值:2 月 30 日、閏年 2 月 29 日邏輯錯誤、月份超出 12 等。直接寫入資料庫會導致約束報錯或數據污染。PHP 內建 checkdate() 函數專門解決此問題,無需手寫複雜正則或閏年判斷邏輯。
checkdate(int $month, int $day, int $year): bool
true,否則 falsecheckdate() 內部實現了完整的格里高利曆規則:
<?php
function validate_date_input(int $month, int $day, int $year): array {
$result = [
'valid' => false,
'date' => null,
'error' => '',
];
// 1. 基礎範圍檢查(可選,checkdate 內部已處理)
if ($year < 1 || $year > 32767 || $month < 1 || $month > 12 || $day < 1 || $day > 31) {
$result['error'] = '參數超出合理範圍';
return $result;
}
// 2. 核心驗證
if (checkdate($month, $day, $year)) {
$result['valid'] = true;
// 生成標準日期字符串
$result['date'] = sprintf('%04d-%02d-%02d', $year, $month, $day);
} else {
$result['error'] = '日期不存在(如 2月30日、非閏年2月29日等)';
}
return $result;
}
// 測試用例
$test_cases = [
[2, 29, 1900], // 非閏年(1900 不能被 400 整除)
[2, 29, 2000], // 閏年(能被 400 整除)
[2, 29, 2100], // 非閏年(能被 100 整除但不能被 400 整除)
[4, 30, 2008], // 合法
[4, 31, 2008], // 4 月無 31 日
[13, 1, 2024], // 月份非法
[1, 0, 2024], // 日期為 0
];
foreach ($test_cases as [$m, $d, $y]) {
$res = validate_date_input($m, $d, $y);
$status = $res['valid'] ? '✅ 合法' : '❌ 非法';
echo "{$y}-{$m}-{$d} => {$status} ";
if (!$res['valid']) echo "原因: {$res['error']}";
echo "\n";
}
1900-2-29 => ❌ 非法 原因: 日期不存在(如 2月30日、非閏年2月29日等) 2000-2-29 => ✅ 合法 2100-2-29 => ❌ 非法 原因: 日期不存在(如 2月30日、非閏年2月29日等) 2008-4-30 => ✅ 合法 2008-4-31 => ❌ 非法 原因: 日期不存在(如 2月30日、非閏年2月29日等) 2024-13-1 => ❌ 非法 原因: 參數超出合理範圍 2024-1-0 => ❌ 非法 原因: 參數超出合理範圍
| 場景 | 代碼片段 |
|---|---|
| 表單驗證(Laravel/CodeIgniter/原生) | 'birthday' => 'required|date_format:Y-m-d|checkdate' (需自定義規則調用 checkdate) |
| 日期選擇器前端回傳值二次校驗 | 前端攔截不代表後端可信,必須服務端再次 checkdate() |
| 導入 Excel/CSV 日期列清洗 | 遍歷行,拆分年月日調用 checkdate(),標記異常行 |
| 生成合法日期範圍(如下拉選單) | 雙層循環年月,內層 for($d=1;$d<=31;$d++) if(checkdate($m,$d,$y)) $days[]=$d; |
function validate_date_advanced(string $date_str, string $format = 'Y-m-d'): ?DateTime {
$dt = DateTime::createFromFormat($format, $date_str);
// createFromFormat 遇到 2024-02-31 會自動溢出變成 2024-03-02,需額外校驗
if ($dt === false) return null;
// 嚴格模式:格式化後必須與原字符串一致(排除溢出情況)
if ($dt->format($format) !== $date_str) return null;
// 額外業務邏輯:如不能早於今天、不能晚於 100 年後等
if ($dt < new DateTime('today')) return null;
return $dt;
}
checkdate() 是 PHP 官方提供的最權威、最高效的日期合法性檢查函數。DateTime::createFromFormat() 可實現字符串格式與邏輯的雙重驗證。