Jackson 的 JsonFormat 对旧日期 API 和 ZonedDateTime 的行为不一致

Inconsistent behaviour of Jackson's JsonFormat for Old Date API and ZonedDateTime

我试图在 POJO class 中使用 Jackson 的 JsonFormat 注释将 ZonedDateTime 序列化到不同的时区。 (假设ZonedDateTimeZone中的时区是ET,我想序列化成UTC)

我的POJO:

package com.example.demo;

import com.fasterxml.jackson.annotation.JsonFormat;
import java.util.Date;

import java.time.ZonedDateTime;

package com.example.demo;

import com.fasterxml.jackson.annotation.JsonFormat;
import java.util.Date;

import java.time.ZonedDateTime;

public class POJO {


    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSSZ", timezone = "UTC")
    private ZonedDateTime zonedDateTimeWithPatternAndTimeZoneInJsonFormat;

    @JsonFormat(timezone = "UTC")
    private ZonedDateTime zonedDateTimeWithTimeZoneInJsonFormat;

    private ZonedDateTime zonedDateTime;


    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSSZ", timezone = "UTC")
    private Date oldDateWithTimeZoneAndPatternInJsonFormat;

    @JsonFormat(timezone = "UTC")
    private Date oldDateWithTimeZoneInJsonFormat;

    private Date oldDate;

    //  getters and setters below


}




主要方法

package com.example.demo;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;

import java.time.*;
import java.util.TimeZone;

package com.example.demo;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

//@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) throws JsonProcessingException {

        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.registerModule(new JavaTimeModule());
        objectMapper.setTimeZone(TimeZone.getDefault());
        objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);

        LocalDateTime ldt = LocalDateTime.of(2020, Month.JUNE.getValue(), 12, 15, 0 , 0);
        ZonedDateTime zdt = ZonedDateTime.of(ldt, ZoneId.of("America/New_York"));
        Date oldDate = Date.from(zdt.toInstant());

        System.out.println("old Date with System's time zone" + oldDate);

        POJO pojo = new POJO();
        pojo.setOldDate(oldDate);
        pojo.setOldDateWithTimeZoneAndPatternInJsonFormat(oldDate);
        pojo.setOldDateWithTimeZoneInJsonFormat(oldDate);

        pojo.setZonedDateTimeWithPatternAndTimeZoneInJsonFormat(zdt);
        pojo.setZonedDateTimeWithTimeZoneInJsonFormat(zdt);
        pojo.setZonedDateTime(zdt);

        System.out.println(objectMapper.writeValueAsString(pojo));

    }


}

输出:

old Date with System's time zoneSat Jun 13 00:30:00 IST 2020

{
    "zonedDateTimeWithPatternAndTimeZoneInJsonFormat": "2020-06-12 19:00:00.000+0000",
    "zonedDateTimeWithTimeZoneInJsonFormat": "2020-06-12T15:00:00-04:00",
    "zonedDateTime": "2020-06-12T15:00:00-04:00",
    "oldDateWithTimeZoneAndPatternInJsonFormat": "2020-06-12 19:00:00.000+0000",
    "oldDateWithTimeZoneInJsonFormat": "2020-06-12T19:00:00.000+00:00",
    "oldDate": "2020-06-13T00:30:00.000+05:30"
}

正如您在输出中看到的那样,如果我只指定时区而不指定模式,则 oldDateWithTimeZone 将在 UTC 时区中序列化,但对于仅指定时区的 ZonedDateTime 不会在指定时区中序列化它。但是,如果您也指定模式,它会遵守指定的时区。

@JsonFormat(timezone= "UTC") 在序列化日期时设置您的时区,而不是格式。 如果你想改变格式我建议使用

@JsonSerialize(using = CustomDateSerializer.class)

实施CustomDateSerializer

class CustomDateSerializer extends StdSerializer<ZonedDateTime> {


public CustomDateSerializer(Class<ZonedDateTime> t) {
    super(t);
}

public CustomDateSerializer() {
    this(null);
}

@Override
public void serialize(ZonedDateTime value, JsonGenerator gen, SerializerProvider provider) throws IOException {
    LocalDateTime utc = LocalDateTime.ofInstant(value.toInstant(), ZoneId.of("UTC"));
    gen.writeString("##date format here##");
}}

这似乎是 jackson-databind 中的一个问题。在 https://github.com/FasterXML/jackson-databind/issues/2799 上报告了该问题。

如问题评论部分所述,指定时区和模式,即 @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss.SSSZ", timezone = "UTC") 按预期工作。我之前只尝试过时区。