且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

使用 Spring Boot 和 Jackson 的日期时区

更新时间:2022-01-10 22:22:51

A java.util.Date 没有任何时区信息.一旦将 String 反序列化为 Date,偏移量 +0300 就会丢失:日期只保留时间戳值,它不能知道它来自哪个原始时区.

A java.util.Date doesn't have any timezone information. Once you deserialize a String to a Date, the offset +0300 is lost: the date keeps just the timestamp value, and it can't know what's the original timezone it came from.

如果输出必须始终在 +03:00 偏移量中,您可以使用 com.fasterxml.jackson.annotation.JsonFormat在各自的字段中直接设置它> 注释:

If the output must always be in +03:00 offset, you can set it directly in the respective fields, using the com.fasterxml.jackson.annotation.JsonFormat annotation:

@JsonFormat(timezone = "GMT+03:00")
private Date startDateTime;

@JsonFormat(timezone = "GMT+03:00")
private Date endDateTime;

这样,日期字段将始终序列化为 +03:00 偏移量:

With this, the date fields will always be serialized to +03:00 offset:

{
  "startDateTime":"2017-10-09T22:43:07.109+0300",
  "endDateTime":"2017-10-09T21:40:07.109+0300"
}

如果输入可以在任何其他偏移量中(不仅是 +03:00)并且您想保留它,java.util.Date 不是理想的类型.一种替代方法是使用 Jackson Modules Java 8,如果您使用的是 Java >= 8.


If the inputs can be in any other offset (not only +03:00) and you want to preserve it, the java.util.Date isn't the ideal type. One alternative is to use Jackson Modules Java 8, if you're using Java >= 8.

对于 Java 6 和 7,有 ThreeTen Backport 和相应的 Jackson 模块 - 不过我还没有测试过,但代码可能相似,因为 ThreeTen Backport 包含相同的类和方法,只有包不同 - (在 Java 8 中是 java.time,而在 ThreeTen Backport 中是 org.threeten.bp).

For Java 6 and 7, there's the ThreeTen Backport and the corresponding Jackson module - I haven't tested, though, but the code might be similar, as the ThreeTen Backport contains the same classes and methods, only the package is different - (in Java 8 is java.time and in ThreeTen Backport is org.threeten.bp).

要保留日期、时间和偏移量,***的替代方法是 OffsetDateTime 类.所以你只需要改变字段类型并为其设置相应的格式即可:

To preserve the date, time and offset, the best alternative is the OffsetDateTime class. So you just need to change the fields type and set the corresponding format to it:

@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXX")
private OffsetDateTime startDateTime;

@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXX")
private OffsetDateTime endDateTime;

在对象映射器中,您还必须注册 JavaTimeModule 并禁用 ADJUST_DATES_TO_CONTEXT_TIME_ZONE 功能,因此保留偏移量(默认行为是转换为 Jackson 上下文的时区,这可能与输入中使用的不同 - 通过禁用此功能,偏移量将被保留).

In the object mapper, you must also register the JavaTimeModule and disable the ADJUST_DATES_TO_CONTEXT_TIME_ZONE feature, so the offsets are preserved (the default behaviour is to convert to Jackson context's timezone, which might not be the same used in the inputs - by disabling this, the offset is preserved).

您可以使用 JacksonConfigurator(如在本答案中解释)并进行这些配置:

You can use a JacksonConfigurator (as explained in this answer) and do these configurations:

ObjectMapper om = new ObjectMapper();
om.registerModule(new JavaTimeModule());
om.configure(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, false);

这个配置通常就足够了,但您也可以将SerializationFeature.WRITE_DATES_AS_TIMESTAMPS 设置为false,以防万一.

This configuration is usually enough, but you can also set SerializationFeature.WRITE_DATES_AS_TIMESTAMPS to false as well, just in case.

如果您仍然需要使用 java.util.Date,您可以使用 API 进行相互转换.在 Java 8 中,有新的 Date.from 方法:

If you still need to work with java.util.Date, you can use the API to convert from/to it. In Java 8, there's the new Date.from method:

// convert to java.util.Date
public Date getStartAsJavaUtilDate() {
    return Date.from(startDateTime.toInstant());
}

在 ThreeTen Backport 中,有 org.threeten.bp.DateTimeUtils 类:

And in ThreeTen Backport, there's the org.threeten.bp.DateTimeUtils class:

// convert to java.util.Date
DateTimeUtils.toDate(startDateTime.toInstant());

Date 转换回 OffsetDateTime,不过,这更棘手.Date 对象没有时区信息,因此无法知道原始偏移量.一种替代方法是将原始偏移量保留在单独的变量中:

To convert a Date back to OffsetDateTime, though, it's more tricky. The Date object has no timezone information, so it can't know the original offset. One alternative is to keep the original offset in a separate variable:

// keep the original offset
ZoneOffset startDateOffset = startDateTime.getOffset();

然后,您可以将Date转换为Instant,然后再转换为原始偏移量:

Then, you can convert the Date to Instant, and then convert it to the original offset:

// convert java.util.Date to original offset (Java 8)
startDateTime = date.toInstant().atOffset(startDateOffset);

// ThreeTen Backport
startDateTime = DateTimeUtils.toInstant(date).atOffset(startDateOffset);