Java日期验证中setLenient(false)未拦截非法格式的问题排查
为什么setLenient(false)没能阻止"18-12-18"通过yyyy-MM-dd格式的日期验证?
我来帮你拆解这个问题的核心原因,其实这是SimpleDateFormat的一个容易被忽略的解析规则导致的:
1. 两位年份会被自动补全为四位年份
SimpleDateFormat在解析日期时,默认会把两位数字的年份自动补全为四位数字,这个行为和setLenient(false)的开关无关。它的补全逻辑基于Calendar的两位年份起始范围(默认是1970年):
- 如果两位年份 >= 70,会被解析为
19xx(比如"70"→1970) - 如果两位年份 < 70,会被解析为
20xx(比如"18"→2018)
所以你输入的"18-12-18"会被自动解析为2018-12-18,这本身是一个合法的日期,自然不会触发严格模式的校验失败。
2. setLenient(false)的作用范围有限
这个方法的作用是关闭宽松的日期调整逻辑,比如:
- 宽松模式下,"2023-02-30"会被自动调整为"2023-03-02"
- 严格模式下,这种无效日期会直接抛出
ParseException
但它管不了年份位数的解析规则——两位年份补全是SimpleDateFormat的默认解析行为,不属于“宽松调整”的范畴,所以即使你关闭了宽松模式,这个补全依然会生效。
解决方案:严格限制年份必须为四位数字
如果你想要严格验证日期格式必须是yyyy-MM-dd(即年份必须是四位),推荐使用Java 8+引入的DateTimeFormatter(它线程安全,且默认严格解析,不会自动补全两位年份):
import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; class Ideone { public static void main(String[] args) { String dateFormat = "yyyy-MM-dd"; String dateToValidate = "18-12-18"; if (!isThisDateValid(dateToValidate, dateFormat)) { System.out.println("Date is invalid"); } else { System.out.println("Date is valid"); } } public static boolean isThisDateValid(String dateToValidate, String dateFormat) { if (dateToValidate == null) { return false; } DateTimeFormatter formatter = DateTimeFormatter.ofPattern(dateFormat); try { // 严格解析,不允许两位年份 LocalDate.parse(dateToValidate, formatter); return true; } catch (DateTimeParseException e) { return false; } } }
如果一定要继续使用SimpleDateFormat,你可以在解析前手动校验输入字符串的格式(比如检查年份部分是否为四位),或者修改两位年份的解析范围让补全后的日期无效,但这种方式比较繁琐,不如新API简洁可靠。
内容的提问来源于stack exchange,提问作者JackTheKnife
相关产品推荐
相关产品推荐

