| 格式範例 | 正則表達式 | 說明 |
|---|---|---|
2023-12-31 | ^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$ | ISO 日期,基礎校驗 |
31/12/2023 | ^(0[1-9]|[12]\d|3[01])/(0[1-9]|1[0-2])/\d{4}$ | 英式日期 |
2023-12-31 23:59:59 | ^\d{4}-\d{2}-\d{2} ([01]\d|2[0-3]):[0-5]\d:[0-5]\d$ | 日期時間秒級 |
12:30 PM | ^(0?[1-9]|1[0-2]):[0-5]\d\s?(AM|PM)$ | 12 小時制 |
23:59:59 | ^([01]\d|2[0-3]):[0-5]\d:[0-5]\d$ | 24 小時制含秒 |
純正則實現極其冗長(見文末附錄),工程實踐建議:正則做格式預檢 + 程式碼做邏輯校驗。
public class DateTimeValidator {
// 常用格式正則(預編譯)
private static final Pattern ISO_DATE = Pattern.compile("^\\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01])$");
private static final Pattern ISO_DATETIME = Pattern.compile("^\\d{4}-\\d{2}-\\d{2} ([01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d$");
private static final Pattern CHINESE_DATE = Pattern.compile("^\\d{4}年(0?[1-9]|1[0-2])月(0?[1-9]|[12]\\d|3[01])日$");
public static boolean isValidDate(String input, Pattern pattern, DateTimeFormatter formatter) {
if (input == null) return false;
if (!pattern.matcher(input).matches()) return false; // 格式快速失敗
try {
LocalDate.parse(input, formatter); // 語義校驗(閏年、月天數)
return true;
} catch (DateTimeParseException e) {
return false;
}
}
// 使用範例
public static void main(String[] args) {
System.out.println(isValidDate("2023-02-29", ISO_DATE, DateTimeFormatter.ISO_LOCAL_DATE)); // false
System.out.println(isValidDate("2024-02-29", ISO_DATE, DateTimeFormatter.ISO_LOCAL_DATE)); // true
System.out.println(isValidDate("2023-13-01", ISO_DATE, DateTimeFormatter.ISO_LOCAL_DATE)); // false (正則攔截)
}
}
Pattern DATE_NAMED = Pattern.compile("^(?<year>\\d{4})-(?<month>0[1-9]|1[0-2])-(?<day>0[1-9]|[12]\\d|3[01])$");
Matcher m = DATE_NAMED.matcher("2023-12-31");
if (m.matches()) {
int year = Integer.parseInt(m.group("year")); // 2023
int month = Integer.parseInt(m.group("month")); // 12
}
// 匹配 2023-12-31 或 2023/12/31 或 2023.12.31,但分隔符必須一致
Pattern CONSISTENT_SEP = Pattern.compile("^\\d{4}([-/.])(0[1-9]|1[0-2])\\1(0[1-9]|[12]\\d|3[01])$");
// \\1 反向引用第一個分隔符
Pattern 為不可變、執行緒安全,**靜態常量預編譯**,避免重複編譯開銷。Matcher 非執行緒安全,**每次使用新建**或 ThreadLocal 復用。^(
# 31 天的月份
(?:(?:31([-/.])(?:0?[13578]|1[02]))\1)
|
# 30 天的月份
(?:(?:29|30)([-/.])(?:0?[13-9]|1[0-2])\2)
|
# 2 月
(?:29([-/.])0?2\3(?:(?:(?:1[6-9]|[2-9]\d)(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))
|
# 1-28 號
(?:0?[1-9]|1\d|2[0-8])([-/.])(?:0?[1-9]|1[0-2])\4
)
(?:1[6-9]|[2-9]\d)\d{2}$
警告:此類正則不可讀、難維護、效能差(回溯風險)。**生產環境請採用「正則預檢 + 時間庫解析」雙重校驗**。
yyyy-MM-dd / yyyy-MM-ddTHH:mm:ss),避免多格式混亂。@Pattern(regexp=...) 註解做格式檢查 + LocalDate.parse() 做語義檢查。DATE/DATETIME/TIMESTAMP 型別,勿存字串。