如何在 spring-mvc 中跳过杰克逊时区校正?

Posted

技术标签:

【中文标题】如何在 spring-mvc 中跳过杰克逊时区校正?【英文标题】:How to skip jackson timezone correction in spring-mvc? 【发布时间】:2018-10-07 07:49:19 【问题描述】:

我想将jackson 配置为输出具有以下格式的任何日期/时间值:

spring.jackson.date-format=yyyy-MM-dd'T'HH:mm:ss

我正在获取许多数据库行并将它们作为json 映射返回。

@RestController
public class MyService 
    @GetMapping
    public List<Map<String, Object>> get(Param params) 
             return jdbcTemplate.queryForList(sql, params);
    

问题:数据库和 jvm 默认时区是Europe/Berlin,因此是 UTC+2。因此,jackson 会先自动将任何数据库接收到的java.sql.Timestamp 转换为 UTC(减去 2 小时),然后通过 json 输出。

mysql 数据库本身中,它是datetime 类型。

但我只想让杰克逊“按原样”输出时间戳,而不需要事先转换!是否可以跳过时区校正?

我只想忽略时区而不进行对话。剪掉就好了。

【问题讨论】:

您的数据库列数据类型是什么?你能避免Timestamp 类吗?它已经过时了。听起来您应该想要来自现代 Java 日期和时间 API java.timeLocalDateTime 数据类型。它没有时区,因此应该防止任何时区问题。 Instant 是另一种选择,具体取决于具体情况。 为了自动获得LocalDateTime 而不是java.sql.Timestamp,我必须使用什么mysql 等效项?事实上,目前它是一个mysql datetime 数据类型。 这个问题可能有你要找的答案Jackson Serialization Ignore Timezone 这个问题我觉得是你需要的。***.com/questions/46151633/… 【参考方案1】:

方法一:设置默认时区

您可以使用ObjectMapper 使用的日期格式设置时区。它将用于Date 和子类:

DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone("Europe/Berlin"));

ObjectMapper mapper = new ObjectMapper();
mapper.setDateFormat(dateFormat);

在Spring应用中,要配置ObjectMapper,可以如下操作:

@Bean
public ObjectMapper objectMapper() 

    DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
    dateFormat.setTimeZone(TimeZone.getTimeZone("Europe/Berlin"));

    ObjectMapper mapper = new ObjectMapper();
    mapper.setDateFormat(dateFormat);
    return mapper;

在 Spring Boot 中,您可以使用属性 spring.jackson.time-zone 来定义时区:

spring.jackson.time-zone: Europe/Berlin

有关常见应用程序属性的更多详细信息,请参阅documentation。

方法 #2:使用 Java 8 日期和时间 API

您可以考虑使用JSR-310 中的LocaDateTime,而不是使用Timestamp。它是在 Java 8 中引入的。“local” 日期和时间类(LocalDateTimeLocalDateLocalTime)不依赖于任何一个地区或时区。来自LocalDateTime 文档:

此类不存储或表示时区。相反,它是对日期的描述,用于生日,结合挂钟上的当地时间。如果没有偏移或时区等附加信息,它不能代表时间线上的瞬间。

answer 将为您提供有关新日期和时间类的更多详细信息。

Jackson 有一个支持 JSR-310 类型的模块。将其添加到您的依赖项中:

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
    <version>2.9</version>
</dependency>

然后在您的ObjectMapper 实例中注册JavaTimeModule 模块:

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

大多数 JSR-310 类型将使用标准的ISO-8601 字符串表示进行序列化。如果需要自定义格式,可以使用自己的序列化器和反序列化器实现。 详情请见documentation。

【讨论】:

好的,但我不能忽略时区吗?我正在连接到多个我事先不知道时区的数据库。据我了解,我必须在您的建议中将数据库时区设置为DateFormat @membersound 如果你有不同的时区,而你故意忽略它们,你会不会得到不一致的结果?我在答案中添加了LocalDateTime 的替代方法。 很好的答案!如果时区信息是相关的,那么使用ZonedDateTime 而不是LocalDateTime 或至少OffsetDateTome 仅根据它们的偏移量来说明数据库之间的时间差异不是更好吗? @CassioMazzochiMolin 在我的情况下,我必须使用旧数据库。我只想通过 json webservice 从数据库中“按原样”输出日期时间值。任何时区校正都无关紧要,我只想通过数据库中存在的 web 服务显示相同的值(并且由于 mysql datetime 类型独立于 timezone,我还想防止 java 中的时区更正。跨度> @membersound You could try to use a converter.【参考方案2】:

最后发现最简单的方法是将杰克逊ObjectMapper(默认使用UTC)时区设置为jvm默认值:

@Bean
public Jackson2ObjectMapperBuilderCustomizer init() 
    return new Jackson2ObjectMapperBuilderCustomizer() 
        @Override
        public void customize(Jackson2ObjectMapperBuilder builder) 
            builder.timeZone(TimeZone.getDefault());
        
    ;

如果有人知道我如何通过使用spring.jackson.time-zone application.property 来实现同样的目标,我将不胜感激。

【讨论】:

你试过在JsonFormat中用timezone="Europe/Berlin"注释java.util.Date的字段吗?

以上是关于如何在 spring-mvc 中跳过杰克逊时区校正?的主要内容,如果未能解决你的问题,请参考以下文章

如何在推送事件中跳过 GitHub Actions 作业?

如何在循环中跳过项目

如何根据某些条件在 MSSQL 游标中跳过一行(迭代)?

如何在 Moshi 中跳过 JSON 属性?

PRISMA:如何在 prisma 常规类型中跳过 id 字段?

WPF - 如何在标签导航中跳过标签?