使用 Spring Boot 和 Jackson 的日期时区

Hab*_*chi 9 java spring date jackson spring-boot

我正在开发一个处理日期的 Spring Boot 应用程序。当我提交一个具有startDateTime和的约会对象endDateTime(两者都是类型java.util.Date)时,我发送的格式如下:

{
    "lastName": "Jhon",
    "firstName": "Doe",
    "email": "jhon.doe@gmail.com",
    "description": "MyDescription",
    "startDateTime": "2017-10-09T22:43:07.109+0300",
    "endDateTime": "2017-10-09T21:40:07.109+0300",
}
Run Code Online (Sandbox Code Playgroud)

当数据保存在数据库中时,它具有正确的时区,当我尝试取回我的数据时,它们在我调试时似乎是正确的,但是,一旦它们被 Jackson 序列化,我就会得到一个输出,这些值作为值:

"startDateTime": "2017-10-09T19:43:07.109+0000",
"endDateTime": "2017-10-09T18:40:07.109+0000",
Run Code Online (Sandbox Code Playgroud)

如何配置 Jackson 以使用存储库中数据附带的时区?

- - - 更新 - - - - -

我尝试了答案,OffsetDateTime但输出很奇怪:

"startDateTime": {
        "offset": {
            "totalSeconds": 7200,
            "id": "+02:00",
            "rules": {
                "fixedOffset": true,
                "transitionRules": [],
                "transitions": []
            }
        },
        "month": "OCTOBER",
        "year": 2017,
        "hour": 21,
        "minute": 49,
        "nano": 654000000,
        "second": 15,
        "dayOfMonth": 9,
        "dayOfWeek": "MONDAY",
        "dayOfYear": 282,
        "monthValue": 10
    }
Run Code Online (Sandbox Code Playgroud)

我想要一些类似的东西:

2017-10-09T22:43:07.109+0300

小智 10

Ajava.util.Date 没有任何时区信息。一旦你反序列化StringDate,补偿+0300丢失:日期只保留时间戳值,它可以不知道什么是它来自原来的时区。

如果输出必须始终处于+03:00偏移量,则可以使用com.fasterxml.jackson.annotation.JsonFormat注释直接在相应字段中进行设置:

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

@JsonFormat(timezone = "GMT+03:00")
private Date endDateTime;
Run Code Online (Sandbox Code Playgroud)

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

{
  "startDateTime":"2017-10-09T22:43:07.109+0300",
  "endDateTime":"2017-10-09T21:40:07.109+0300"
}
Run Code Online (Sandbox Code Playgroud)

如果输入可以处于任何其他偏移量(不仅是+03:00)并且您想保留它,java.util.Date则这不是理想的类型。如果您使用的是 Java >= 8,另一种选择是使用Jackson Modules Java 8 。

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

要保留日期、时间和偏移量,最好的选择是OffsetDateTime类。所以你只需要改变字段类型并为其设置相应的格式:

@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;
Run Code Online (Sandbox Code Playgroud)

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

您可以使用JacksonConfigurator(如本答案中所述)并执行以下配置:

ObjectMapper om = new ObjectMapper();
om.registerModule(new JavaTimeModule());
om.configure(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, false);
Run Code Online (Sandbox Code Playgroud)

这种配置通常是不够的,但你也可以设置SerializationFeature.WRITE_DATES_AS_TIMESTAMPSfalse为好,以防万一。


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

// convert to java.util.Date
public Date getStartAsJavaUtilDate() {
    return Date.from(startDateTime.toInstant());
}
Run Code Online (Sandbox Code Playgroud)

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

// convert to java.util.Date
DateTimeUtils.toDate(startDateTime.toInstant());
Run Code Online (Sandbox Code Playgroud)

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

// keep the original offset
ZoneOffset startDateOffset = startDateTime.getOffset();
Run Code Online (Sandbox Code Playgroud)

然后,您可以将 转换DateInstant,然后将其转换为原始偏移量:

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

// ThreeTen Backport
startDateTime = DateTimeUtils.toInstant(date).atOffset(startDateOffset);
Run Code Online (Sandbox Code Playgroud)