Spring Boot中dd/MM/yyyy格式年份验证失效问题求助
问题分析与解决方案
问题背景
在Spring Boot 2.3.0.RELEASE中自定义日期格式验证时,遇到如下异常情况:
- 当使用
dd/MM/yyyy格式校验时,日(dd)或月(MM)部分输入非数字(如02/1k/2022、k2/12/2022)能被正确拦截; - 但年份(
yyyy)部分输入非数字(如02/12/2k22)时,验证不会触发失效; - 若将日期格式改为
yyyy-MM-dd,年份的非数字输入能被正常校验。
依赖配置:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency>
自定义验证器、注解及实体类字段代码如提问所示。
问题根源
问题出在SimpleDateFormat的解析逻辑上:
- 对于
dd和MM模式,SimpleDateFormat会严格校验输入是否为数字,非数字字符直接抛出ParseException; - 但针对
yyyy模式,它会自动忽略年份部分的非数字字符,仅提取其中的数字内容解析。例如2k22会被解析为2022(跳过k,拼接前后数字),因此不会触发异常,导致验证失效; - 当格式改为
yyyy-MM-dd时,年份位于字符串开头,非数字字符会直接触发解析失败,因此验证正常。
解决方案
替换线程不安全且解析逻辑宽松的SimpleDateFormat,使用Java 8+引入的DateTimeFormatter(线程安全、解析严格)。修改后的验证器代码如下:
public class IsValidDateFormatValidator implements ConstraintValidator<IsValidDateFormat, String> { // 使用严格模式的日期格式化器 private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("dd/MM/uuuu") .withResolverStyle(ResolverStyle.STRICT); @Override public void initialize(IsValidDateFormat arg0) { // 无需初始化操作 } @Override public boolean isValid(String dateStr, ConstraintValidatorContext context) { if (dateStr == null || dateStr.trim().isEmpty()) { return true; // 空值校验交由@NotEmpty注解处理 } // 先校验长度是否符合dd/MM/yyyy的10位格式 if (dateStr.trim().length() != 10) { return false; } try { // 严格解析日期字符串 LocalDate.parse(dateStr, DATE_FORMATTER); return true; } catch (DateTimeParseException e) { return false; } } }
关键优化点:
- 使用
uuuu代替yyyy:uuuu是ISO标准年份,比yyyy更严谨,避免处理公元前年份时的逻辑歧义; - 启用
ResolverStyle.STRICT:强制严格解析规则,确保日期格式完全匹配,任何非预期字符、无效日期(如2月30日)都会触发解析异常; - 采用
LocalDate:不可变且线程安全,避免SimpleDateFormat的线程安全问题,同时API更简洁直观。
内容的提问来源于stack exchange,提问作者M123
相关产品推荐
相关产品推荐

