Spring Boot和Jackson的日期时区

时间:2017-10-09 19:12:44

标签: java spring date spring-boot jackson

我正在开发一个处理日期的spring启动应用程序。当我提交具有startDateTimeendDateTime(两者都是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",
}

当数据在数据库中保留时,它使用正确的时区,当我尝试检索我的数据时,在我调试时它们似乎是正确的,但是一旦它们被Jackson序列化我就有输出以这些作为价值:

"startDateTime": "2017-10-09T19:43:07.109+0000",
"endDateTime": "2017-10-09T18:40:07.109+0000",

如何配置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
    }

我希望有类似的东西:

  

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

1 个答案:

答案 0 :(得分:3)

java.util.Date doesn't have any timezone information。将String反序列化为Date后,偏移量+0300将丢失:日期只保留时间戳值,并且无法知道它来自的原始时区。< / p>

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

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

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

这样,日期字段将始终序列化为+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&gt; = 8。

对于Java 6和7,有ThreeTen Backport和相应的Jackson module - 我还没有测试过,但代码可能类似,因为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;

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

您可以使用JacksonConfigurator(作为explained in this answer)并执行以下配置:

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

此配置通常已足够,但您也可以将SerializationFeature.WRITE_DATES_AS_TIMESTAMPS设置为false,以防万一。

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

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

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

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

要将Date转换回OffsetDateTime,它会更棘手。 Date对象没有时区信息,因此无法知道原始偏移量。另一种方法是将原始偏移量保存在单独的变量中:

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

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

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

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