日期字符串解析失败求助:如何正确格式化编码日期参数?
问题分析与解决方案
错误原因
日期解析失败有两个核心问题:
- URL编码未处理:传入的字符串
2022-10-30T13%3A28%3A41.530530中的%3A是URL编码后的冒号(:),直接解析会因字符不匹配报错。 - 格式器不匹配:代码中使用的
yyyy-MM-dd HH:mm:ss格式,与实际传入的yyyy-MM-dd'T'HH:mm:ss.SSSSSS格式(带T分隔符、毫秒数)完全不兼容。
另外,你添加的@JsonFormat注解无效,因为该注解仅作用于JSON请求体的字段反序列化,而你的日期参数是URL请求参数,不受Jackson的JSON解析规则管控。
修复步骤
1. 先对日期字符串做URL解码
使用URLDecoder将编码后的字符还原为原始格式:
import java.net.URLDecoder; import java.nio.charset.StandardCharsets; // 解码示例 String decodedStart = URLDecoder.decode(params.getStart(), StandardCharsets.UTF_8); // 解码后得到:2022-10-30T13:28:41.530530
2. 使用匹配的日期格式器解析
由于解码后的字符串符合ISO-8601标准格式,直接使用JDK内置的ISO_LOCAL_DATE_TIME格式器即可,无需自定义格式:
import java.time.format.DateTimeFormatter; // 替换原有的DateTimeFormatter定义 DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
或者如果需要明确指定毫秒位数的自定义格式,也可以使用:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSS");
修改后的完整代码
default Specification<View> getSpecificationByParams(StatsSearchParams params) { return (((root, query, criteriaBuilder) -> { List<Predicate> predicates = new ArrayList<>(); DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME; if (params.getUris() != null) { for (String uri : params.getUris()) { predicates.add(criteriaBuilder.in(root.get("uri")).value(uri)); } } if (params.getStart() != null) { // 先解码再解析 String decodedStart = URLDecoder.decode(params.getStart(), StandardCharsets.UTF_8); predicates.add(criteriaBuilder.greaterThan( root.get("timestamp"), LocalDateTime.parse(decodedStart, formatter))); } if (params.getEnd() != null) { // 先解码再解析 String decodedEnd = URLDecoder.decode(params.getEnd(), StandardCharsets.UTF_8); predicates.add(criteriaBuilder.lessThanOrEqualTo( root.get("timestamp"), LocalDateTime.parse(decodedEnd, formatter))); } return criteriaBuilder.and(predicates.toArray(Predicate[]::new)); })); }
额外优化建议
如果你的统计服务是通过Spring MVC接收请求参数,也可以在StatsSearchParams类的日期字段上使用@DateTimeFormat注解,让框架自动完成解码和格式转换:
public class StatsSearchParams { // 其他字段... @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) private LocalDateTime start; @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) private LocalDateTime end; // getter/setter... }
这种情况下,你可以直接在Specification中使用params.getStart()和params.getEnd(),无需手动解码和解析,代码会更简洁。
内容的提问来源于stack exchange,提问作者Arm
相关产品推荐
相关产品推荐

