Jackson OffsetDateTime 序列化 Z 而不是 +00:00 时区?
Posted
技术标签:
【中文标题】Jackson OffsetDateTime 序列化 Z 而不是 +00:00 时区?【英文标题】:Jackson OffsetDateTime serialization Z instead of +00:00 timezone? 【发布时间】:2020-06-12 13:41:37 【问题描述】:我正在使用带有以下 ObjectMapper 的 Spring Boot:
@Bean
public ObjectMapper objectMapper()
final ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
mapper.setDateFormat(new StdDateFormat().withColonInTimeZone(true)); // Makes no difference to output
mapper.findAndRegisterModules();
return mapper;
当 OffsetDateTimes 被序列化并在响应中返回时,它们的格式如下:
"2020-02-28T12:28:29.01Z"
"2020-02-28T12:36:21.885Z"
我本来希望最后的时区信息看起来像这样:
"2020-02-28T10:41:25.287+00:00"
我在这里有什么遗漏或做错了吗,或者无论如何我可以将时区信息序列化为+00:00
格式而不是885Z
格式?
非常感谢!
【问题讨论】:
它确实包括了时区,只是不是你所期望的。 'Z' 代表祖鲁时间,类似于 +00:00 【参考方案1】:使用预构建格式或向DateTimeFormatter
提供自定义模式有多种可能性。
看看这些(非常简单的)例子:
public static void main(String[] arguments)
Instant now = Instant.now();
ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(now, ZoneId.of("UTC"));
OffsetDateTime offsetDateTime = OffsetDateTime.ofInstant(now, ZoneId.of("UTC"));
System.out.println(zonedDateTime.toString());
System.out.println(offsetDateTime.toString());
System.out.println(zonedDateTime.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
System.out.println(offsetDateTime.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME));
System.out.println(zonedDateTime.format(
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ"))
);
System.out.println(offsetDateTime.format(
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssxxx")
)
);
我认为您所期望的是最后一个,以xxx
结尾的模式,这导致偏移量始终以HH:mm
的形式显示,代码示例的输出是:
2020-02-28T12:49:02.388Z[UTC]
2020-02-28T12:49:02.388Z
2020-02-28T12:49:02.388Z[UTC]
2020-02-28T12:49:02.388Z
2020-02-28T12:49:02+0000
2020-02-28T12:49:02+00:00
【讨论】:
【参考方案2】:新的 Java 8 Time API 提供了一个DateTimeFormatter
,您可以在其中将格式的结尾设置为一个或多个x
或X
。根据api描述:
偏移 X 和 x:这会根据模式字母的数量来格式化偏移。一个字母仅输出小时,例如“+01”,除非分钟不为零,在这种情况下也会输出分钟,例如“+0130”。两个字母输出小时和分钟,不带冒号,如'+0130'。三个字母输出小时和分钟,带有冒号,例如'+01:30'。四个字母输出小时和分钟以及可选的秒,不带冒号,例如'+013015'。五个字母输出小时和分钟以及可选的秒,带有冒号,例如'+01:30:15'。六个或更多字母会引发 IllegalArgumentException。当要输出的偏移量为零时,模式字母“X”(大写)将输出“Z”,而模式字母“x”(小写)将输出“+00”、“+0000”或“+00” :00'。
因此,在您的情况下,您的格式字符串应该以 xxx
结尾,对于 +1:30
,例如"yyyy-MM-dd'T'HH:mm:ss.SSSxxx"
和 SSS
总是给出毫秒数。
要将此DateTimeFormatter
与 Jackson 一起使用,您需要定义一个自定义序列化程序
public class DefaultZonedDateTimeSerializer extends JsonSerializer<ZonedDateTime>
private static final DateTimeFormatter ISO_8601_FORMATTER = DateTimeFormatter
.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX")
.withZone(ZoneId.of("UTC"));
@Override
public void serialize(ZonedDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException
if (value == null)
throw new IOException("ZonedDateTime argument is null.");
gen.writeString(ISO_8601_FORMATTER.format(value));
并用
注释你的bean中的各个字段@JsonSerialize(using = DefaultZonedDateTimeSerializer.class)
private ZonedDateTime someTimeProperty;
或者您需要从 DateTimeFormatter
转换为 DateFormat
(较旧,但由 Jackson 使用),如下所述:Using DateTimeFormatter with ObjectMapper
【讨论】:
【参考方案3】:以下步骤解决了这个问题(取自https://***.com/a/41893238/12177456),还要感谢@Ralf Wagner 和@deHaar:
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.6.5</version>
</dependency>
public class OffsetDateTimeSerializer extends JsonSerializer<OffsetDateTime>
private static final DateTimeFormatter ISO_8601_FORMATTER = DateTimeFormatter
.ofPattern("yyyy-MM-dd'T'HH:mm:ssxxx")
.withZone(ZoneId.of("UTC"));
@Override
public void serialize(OffsetDateTime value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException
if (value == null)
throw new IOException("OffsetDateTime argument is null.");
jsonGenerator.writeString(ISO_8601_FORMATTER.format(value));
@Bean
public ObjectMapper objectMapper()
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
objectMapper.registerModule(new JavaTimeModule());
SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(OffsetDateTime.class, new OffsetDateTimeSerializer());
objectMapper.registerModule(simpleModule);
return objectMapper;
【讨论】:
看起来很酷,但没有帮助:2019-11-30T10:30:00.123+03:00
仍然转换为 2019-11-30T07:30:00.123Z
:(
new ObjectMapper(),registerModule(new JavaTimeModule()).configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false).configure(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, false)
实际上解决了我之前评论中的问题。【参考方案4】:
如果您使用的是 JodaDateTime,请尝试以下操作:
@Bean
public ObjectMapper getObjectMapper()
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JodaModule());
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
return objectMapper;
【讨论】:
以上是关于Jackson OffsetDateTime 序列化 Z 而不是 +00:00 时区?的主要内容,如果未能解决你的问题,请参考以下文章
在 Spring REST Controller 中对 DateTime 使用 Jackson 反序列化
为啥 OffsetDateTime 序列化/反序列化结果有差异?
使用 Spring Boot webclient 反序列化 OffsetDateTime
我无法将 '2017-04-04T08:04+0000' 解析为 OffsetDateTime
SpringBoot RestTemplate 忽略 spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS = false