DateTimeFormatter可格式化含字面量Z的ISO-8601日期却无法解析该格式
解决带字面量'Z'的ISO-8601日期解析问题
我完全懂你的困扰——用带字面量'Z'的格式能正常输出符合要求的日期串,但解析时却直接抛出DateTimeException,还没法改格式字符串、也不能调整解析逻辑(因为依赖Jackson)。咱们来一步步搞定这个问题。
问题根源
你用的格式串"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"里,'Z'是字面量字符:格式化时它会被直接输出成字符Z,但解析时,它只会被当作普通字符匹配,不会被识别为UTC时区偏移(+00:00)。这就导致解析后的TemporalAccessor里没有ZoneOffset信息,而OffsetDateTime.from()必须依赖这个信息来构建对象,自然就抛出异常了。
核心解决方案:构建兼容格式化与解析的DateTimeFormatter
既然不能改动格式串里的字面量Z,我们可以用DateTimeFormatterBuilder创建一个既能输出Z,又能在解析时自动补全UTC偏移的格式化器:
// 构建兼容的格式化器 DateTimeFormatter formatter = new DateTimeFormatterBuilder() // 添加日期时间部分的格式 .appendPattern("yyyy-MM-dd'T'HH:mm:ss.SSS") // 添加字面量Z .appendLiteral('Z') // 关键:解析时若未获取到偏移信息,默认使用UTC(偏移0秒) .parseDefaulting(ChronoField.OFFSET_SECONDS, 0) .toFormatter();
测试一下这个格式化器:
- 格式化输出依然是你需要的格式:
2020-01-19T03:06:58.090Z - 解析时,因为我们设置了默认偏移为UTC,
TemporalAccessor会自动带上ZoneOffset.UTC,OffsetDateTime.from()就能成功构建对象:
String string = formatter.format(OffsetDateTime.now()); TemporalAccessor acc = formatter.parse(string); OffsetDateTime time = OffsetDateTime.from(acc); System.out.println(time); // 输出:2020-01-19T03:06:58.090Z
适配Jackson的方案
因为你依赖Jackson,我们可以把这个自定义格式化器配置到Jackson的JavaTimeModule里,让序列化和反序列化都适配这个格式:
// 先构建刚才的格式化器 DateTimeFormatter formatter = new DateTimeFormatterBuilder() .appendPattern("yyyy-MM-dd'T'HH:mm:ss.SSS") .appendLiteral('Z') .parseDefaulting(ChronoField.OFFSET_SECONDS, 0) .toFormatter(); // 配置JavaTimeModule JavaTimeModule module = new JavaTimeModule(); // 自定义序列化器,用我们的格式输出日期 module.addSerializer(OffsetDateTime.class, new OffsetDateTimeSerializer(formatter)); // 自定义反序列化器,用我们的格式解析日期 module.addDeserializer(OffsetDateTime.class, new OffsetDateTimeDeserializer(formatter)); // 注册到ObjectMapper ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(module);
这样Jackson在序列化OffsetDateTime时会输出带字面量Z的格式,反序列化时也能正确解析成OffsetDateTime对象,完美适配你的需求。
内容的提问来源于stack exchange,提问作者johnny
相关产品推荐
相关产品推荐

