表单datetime-local值如何适配Spring指定格式的Date类型?
问题描述
表单使用如下datetime-local输入框:
<input type="datetime-local" id="enddate" name="endDate" class="form-control" placeholder="endDate">
Spring实体类字段定义:
@DateTimeFormat(pattern = "dd.MM.yyyy hh:mm:ss") private Date endDate;
提交表单后出现类型转换错误,完整错误信息翻译如下:
2022-10-17 12:34:20.614 WARN 12128 --- [nio-8081-exec-4] .w.s.m.s.DefaultHandlerExceptionResolver : 已解析 [org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1个错误
对象'threads'的字段'endDate'存在字段错误: 被拒绝的值 [2022-10-28T12:34];
错误码 [typeMismatch.threads.endDate,typeMismatch.endDate,typeMismatch.java.time.LocalDateTime,typeMismatch]; 参数 [org.springframework.context.support.DefaultMessageSourceResolvable: 错误码 [threads.endDate,endDate]; 参数 []; 默认消息 [endDate]]; 默认消息 [无法将属性'endDate'的类型'java.lang.String'转换为所需类型'java.time.LocalDateTime'; 嵌套异常为org.springframework.core.convert.ConversionFailedException: 无法将值'2022-10-28T12:34'从类型[java.lang.String]转换为类型[@org.springframework.format.annotation.DateTimeFormat java.time.LocalDateTime]; 嵌套异常为java.lang.IllegalArgumentException: 解析值[2022-10-28T12:34]失败]]
要求必须保留@DateTimeFormat注解和Date类型,需解决表单输入值到Date类型的映射问题。
解决方案
方法1:给特定字段注册自定义属性编辑器
通过全局Controller增强类,专门为endDate字段注册属性编辑器,直接解析前端传入的yyyy-MM-ddTHH:mm格式字符串为Date对象,原注解可保留用于输出时的格式化。
import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.annotation.ControllerAdvice; import java.beans.PropertyEditorSupport; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; @ControllerAdvice public class GlobalBindingInitializer { @InitBinder public void initBinder(WebDataBinder binder) { binder.registerCustomEditor(Date.class, "endDate", new PropertyEditorSupport() { private final SimpleDateFormat inputFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm"); @Override public void setAsText(String text) throws IllegalArgumentException { if (text == null || text.isEmpty()) { setValue(null); return; } try { Date parsedDate = inputFormat.parse(text); setValue(parsedDate); } catch (ParseException e) { throw new IllegalArgumentException("日期格式无效,需符合yyyy-MM-dd'T'HH:mm格式", e); } } }); } }
方法2:全局注册String转Date转换器
实现Spring的Converter接口,全局处理所有String到Date的转换,前端传入的yyyy-MM-ddTHH:mm格式字符串会被自动解析为Date对象,原@DateTimeFormat注解仍负责输出格式化。
import org.springframework.core.convert.converter.Converter; import org.springframework.stereotype.Component; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; @Component public class StringToDateConverter implements Converter<String, Date> { private final SimpleDateFormat inputFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm"); @Override public Date convert(String source) { if (source == null || source.isEmpty()) { return null; } try { return inputFormat.parse(source); } catch (ParseException e) { throw new IllegalArgumentException("无效的日期格式: " + source, e); } } }
方法3:前端提交前转换格式(不推荐)
若允许修改前端代码,可在表单提交前将datetime-local的值转换为dd.MM.yyyy hh:mm:ss格式,但需处理24小时转12小时的逻辑,较为繁琐:
document.querySelector('form').addEventListener('submit', function(e) { const endDateInput = document.getElementById('enddate'); if (endDateInput.value) { const [datePart, timePart] = endDateInput.value.split('T'); const [year, month, day] = datePart.split('-'); const [hour, minute] = timePart.split(':'); // 转换为12小时制 let formattedHour = parseInt(hour) % 12; formattedHour = formattedHour === 0 ? 12 : formattedHour; endDateInput.value = `${day}.${month}.${year} ${formattedHour}:${minute}:00`; } });
注意事项
- 若使用Spring Boot 2.x及以上版本,
@DateTimeFormat默认适配Java 8日期API,但你坚持使用java.util.Date,上述两种后端方案是最稳妥的选择。 - 方法1仅针对
endDate字段生效,方法2全局处理所有String到Date的转换,可根据实际需求选择。
内容的提问来源于stack exchange,提问作者quwertysokman

