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")
按预期工作。我之前只尝试过时区。
我试图在 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")
按预期工作。我之前只尝试过时区。