用Jackson @JsonFormat设置日期,为什么会少一天?

31

我在我的项目中使用了Spring Boot和Spring Date Rest。这个项目有一个对象,我使用注释@JsonFormat来格式化将从Json接收的日期字段。日期字段的格式为“dd/MM/yyyy”。当我在我的json中发送值“08/07/1980”时,Jackson会将其转换为值“07/07/1980”。

问题在于@JsonFormat设置日期少了一天

这是我的源代码

@Temporal(TemporalType.DATE)
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd/MM/yyyy", locale = "pt-BR", timezone = "UTC")
private Date birthDate;

感谢


1
我猜你的意思是在向后端执行请求时。客户端是浏览器还是某种 REST 工具?客户端和服务器是否处于同一时区? - franDayz
1
我的第一个猜测是,由于您正在使用UTC并省略小时和秒,所以应用巴西和UTC之间的偏移量导致时间信息丢失。作为快速尝试,您可以从注释中省略locale属性,并改用此模式“yyyy-MM-dd'T'HH:mm:ss.SSSZ”,该模式包括时区信息和毫秒,因此客户端选择的表示方式无关紧要。 - franDayz
5个回答

41

使用此解决方案,它比我的解决方案更有效和现代化:https://dev59.com/YFwZ5IYBdhLWcg3wINPQ#45456037

感谢@Benjamin Lucidarme。

我使用以下方法解决了我的问题:

@Temporal(TemporalType.DATE)
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd/MM/yyyy", locale = "pt-BR", timezone = "Brazil/East")
private Date birthDate;

我将时区更改为“巴西/东部”或“美洲/圣保罗”,现在工作正常。

谢谢


2
同样的问题,同样的解决方案 :)) - Samrat
1
我将时区设置为“CET”,在欧洲生活和工作都很顺利,谢谢。 - batt

19

@William的答案是有效的,但您应该将这些行添加到您的application.properties文件中:

spring.jackson.time-zone=Brazil/East
spring.jackson.locale=pt-BR

这样一来,你只需表示时区和语言环境一次,就可以适用于应用程序的所有日期。


3
谢谢,您的解决方案更有效和现代化。 - William Miranda de Jesus
1
如此简单而优雅的解决方案。谢谢! - Юрий Яхница

5

我建议将ObjectMapper的时区设置为JVM默认时区:

    ObjectMapper objectMapper = new ObjectMapper();
    //Set default time zone as JVM timezone due to one day difference between original date and formatted date.
    objectMapper.setTimeZone(TimeZone.getDefault());

如果你不知道服务器环境使用的是哪个时区,那么这是一个更好的解决方案。

spring-boot 环境中,你可以覆盖默认的 JacksonAutoConfiguration:

@Bean
ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
    return builder.createXmlMapper(false)
            // Set timezone for JSON serialization as system timezone
            .timeZone(TimeZone.getDefault())
            .build();
}

3
在客户端和服务器端都要这样注释您的日期字段:
@JsonDeserialize(using = JsonDateDeserializer.class)
@JsonSerialize(using = JsonDateSerializer.class)
private Date birthDate;

同时在两端加入以下内容以实现序列化和反序列化:

public class JsonDateSerializer extends JsonSerializer<Date> {
    SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy");

    @Override
    public void serialize(final Date date, final JsonGenerator gen, final SerializerProvider provider) throws IOException, JsonProcessingException {

        String dateString = format.format(date);
        gen.writeString(dateString);
    }

}


public class JsonDateDeserializer extends JsonDeserializer<Date> {

    SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy");

    @Override
    public Date deserialize(final JsonParser jp, final DeserializationContext ctxt) throws IOException, JsonProcessingException {
        if (jp.getCurrentToken().equals(JsonToken.VALUE_STRING)) {
            try {
                Date date = format.parse(jp.getText().toString());
                return date;
            } catch (ParseException e) {
                //e.printStackTrace();
            }
        }
        return null;
    }

}

2
我也遇到了类似的问题。我猜这个问题所有的答案都是特定于上面的问题,我的解决方案将解释这个问题并提供通用解决方案。
当您在相同的日期中使用2个不同的时区时,您可能会遇到此问题。 当您执行new Date()时,它会使用您的默认时区,除非您明确指定时区。让我通过一个代码片段来向您解释(您在印度,当前日期为(2021年9月5日,IST午夜12点))。
// 09/05/2021 00:00:00 IST
Date birthDate = new Date();

现在,当将birthDate设置到模型中并使用JsonFormat序列化时,默认情况下JsonFormat使用UTC时区。

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd/MM/yyyy")
private Date birthDate;

序列化将导致日期为08/05/2021,而非09/05/2021
让我们使用带有时区的日期,如08/05/2021 UTC09/05/2021 IST
让我们也为日期添加时间,如08/05/2021 18:30:00 UTC09/05/2021 00:00:00 IST
现在您知道为什么会发生这种情况了,如果您看到日期是正确的,但具有不同的时区。有两种方法可以解决此问题:
  1. 使用相同的时区
  2. 在序列化日期时添加时间和时区,例如08/05/2021 18:30:00 UTC09/05/2021 00:00:00 IST

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接