驗證 YYYY-MM-DD 格式日期,年份範圍 1800-3999,需精確校驗閏年規則:
將年份分為閏年與平年兩大類,分別構造年份正則,再組合月份/日期部分。
// 非世紀閏年:尾數 04,08,12...96 (不含 00)
String leap1 = "((1[8-9]|[2-3]\\d)(0[48]|[2468][048]|[13579][26]))";
// 世紀閏年:2000,2400,2800,3200,3600
String leap2 = "((20|24|28|32|36)00)";
// 合併閏年
String leapYear = "(" + leap1 + "|" + leap2 + ")";
// 非世紀平年:尾數 01-99 除去閏年尾數
String noleap1 = "((1[8-9]|[2-3]\\d)(0[1235679]|[13579][01345789]|[2468][1235679]))";
// 世紀平年:1800,1900,2100...3900 (除 2000,2400...)
String noleap2 = "((18|19|21|22|23|25|26|27|29|30|31|33|34|35|37|38|39)00)";
// 合併平年
String noleapYear = "(" + noleap1 + "|" + noleap2 + ")";
// 31天月份:01,03,05,07,08,10,12
String month31 = "(0[13578]|1[02])-([0-2]\\d|3[01])";
// 30天月份:04,06,09,11
String month30 = "(0[469]|11)-([0-2]\\d|30)";
// 2月平年:01-28
String febNormal = "02-([0-1]\\d|2[0-8])";
// 2月閏年:01-29
String febLeap = "02-([0-1]\\d|2[0-9])";
String dateRegex =
"^(" + leapYear + ")-(" + febLeap + "|" + month30 + "|" + month31 + ")" +
"|^(" + noleapYear + ")-(" + febNormal + "|" + month30 + "|" + month31 + ")" +
"$";
^(
# 閏年
(
(1[8-9]|[2-3]\d)(0[48]|[2468][048]|[13579][26]) # 非世紀閏年
| (20|24|28|32|36)00 # 世紀閏年
)
-
(
02-(0[1-9]|1\d|2[0-9]) # 閏年2月 01-29
| (0[13578]|1[02])-(0[1-9]|[12]\d|3[01]) # 31天月
| (0[469]|11)-(0[1-9]|[12]\d|30) # 30天月
)
|
# 平年
(
(1[8-9]|[2-3]\d)(0[1235679]|[13579][01345789]|[2468][1235679]) # 非世紀平年
| (18|19|21|22|23|25|26|27|29|30|31|33|34|35|37|38|39)00 # 世紀平年
)
-
(
02-(0[1-9]|1\d|2[0-8]) # 平年2月 01-28
| (0[13578]|1[02])-(0[1-9]|[12]\d|3[01]) # 31天月
| (0[469]|11)-(0[1-9]|[12]\d|30) # 30天月
)
)$
Pattern pattern = Pattern.compile(DATE_REGEX);
Matcher m = pattern.matcher("2000-02-29");
boolean valid = m.matches(); // true
const regex = new RegExp(DATE_REGEX);
regex.test("1900-02-29"); // false
import re
re.fullmatch(DATE_REGEX, "2024-02-29") # 匹配
LocalDate.parse、datetime.strptime)。本文提供了1800-3999 年嚴格遵循格里曆閏年規則的日期正則表達式,分為模塊化構建版與單一優化版,適用於 Java、JS、Python、PCRE 等引擎。實際項目中推薦「正則管格式,代碼管邏輯」的分層校驗策略。