【问题标题】:Spring REST LocalDate UTC differs of one daySpring REST LocalDate UTC 相差一天
【发布时间】:2017-12-03 11:49:59
【问题描述】:

我正在使用带有 LocalDate 和 LocalDateTime 的 Spring Boot 1.5.4、Hibernate 5.2.10、Spring Data REST、HATEOAS、JDK8。 我的电脑在 CEST 时区,但我希望应用程序在 UTC 中工作,所以我在 application.properties 中设置:

spring.datasource.url=jdbc:mysql://localhost:3306/database?useLegacyDatetimeCode=false&serverTimezone=UTC&useSSL=true
spring.jpa.hibernate.jdbc.time_zone = UTC

根据article,我不想更改 JVM 的时区,因为这似乎不是最佳实践。

Spring Data REST 公开了我的存储库,我使用 Swagger2 来提供一个很好的接口来使用 API。 当我尝试一个端点时,我看到如下内容:

    {
  "_embedded": {
    "dailyCodes": [
      {
        "sid": "d495cdaa-14f2-44cb-a98f-8aa6ddd43d91",
        "createdDate": "2017-06-28T16:20:01",
        "lastModifiedDate": "2017-06-28T16:20:01",
        "lastModifiedBy": "admin",
        "date": "2017-06-28",
        "code": "js",
        "new": false,
        "_links": {
          "self": {
            "href": "http://localhost:8080/api/v1/dailyCodes/1"
          },
          "dailyCode": {
            "href": "http://localhost:8080/api/v1/dailyCodes/1"
          }
        }
      }

就像您可以看到日期时间格式很好,并且即使在数据库中实际时间是 14:20:01,它也会显示 CEST 时间。 我想这是错误的,因为我的 REST API 应该在 UTC 中工作。

我怎样才能达到这个结果?

总是在同一个主题上,我有一个 REST 端点(由 Spring Data REST 公开)使用 LocalDate 参数进行搜索;我正在使用

@Transactional
@PreAuthorize("isAuthenticated()")
public interface DailyCodeRepository extends PagingAndSortingRepository<DailyCode, Long> {

@Query("SELECT d FROM DailyCode d WHERE (:code IS NULL or code=:code) AND (:from IS NULL OR date>=:from) AND (:to IS NULL OR date<=:to)")
    public Page<DailyCode> findAllWithParameter(@Param("code") @RequestParam(value = "code", required = false) String code,
            @Param("from") @RequestParam(value = "from", required = false) LocalDate from,
            @Param("to") @RequestParam(value = "to", required = false) LocalDate to, Pageable pageable);
}

在这种情况下,我还有一件奇怪的事情:如果我调用端点传递参数,它们在服务器中到达的时间会少 1 天。此外,接受的日期模式似乎是我的语言环境(意大利语)的 JDK 默认值,但我想这不是最佳实践。

是否有最佳做法可以避免双向日期/时间参数出现问题?

【问题讨论】:

  • 我对 LocalDate 等不是很熟悉。但是jdbc.time_zone参数只影响VM内部和数据库之间的转换。在JDBC驱动中,会有一个Date对象,基本上是era毫秒,没有任何时区信息。通常,当它被格式化为(人类可读的)字符串时,这将根据格式化程序中指定的时区或默认时区来完成。
  • 但是,当它被格式化为作为日期写入数据库时​​,jdbc.time_zone 参数用于此转换。我不确定当 Date 对象转换为 LocalDate 时会发生什么,但我假设您遇到的问题在于那里,而不是 VM 和 DB 之间的转换。

标签: java spring rest spring-mvc spring-data-rest


【解决方案1】:

我同意本文作者The 5 laws of API dates and times 的观点,即我们必须以UTC 存储和返回时间。而“前端”必须自己决定如何根据客户端时区转换时间值。

为了实现这一点(以 UTC 存储和返回时间),我们设置 JVM 参数 -Duser.timezone=UTC 或将 spring.jpa.properties.hibernate.jdbc.time_zone=UTC 添加到 application.properties(从 Hibernate 5.2.3.Final 开始)。然后将我们实体的时间字段从LocalDateTime(不存储时区信息)更改为ZonedDateTime类型。

之后,时间值将独立于启动应用程序的计算机的本地时区以 UTC 存储,SDR 将以 ISO8601 形式返回这些值:@987654327 @

但如果需要返回特定时区的时间值,我们必须为所有时间字段设置@JsonFormat 注解:

@JsonFormat(timezone = "Europe/Rome", pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ")
private ZonedDateTime createdDate;

我们可以在我们的应用程序中定义常量TIME_ZONE并在@JsonFormat注解中设置它:@JsonFormat(timezone = Application.TIME_ZONE, ...)

然后我们得到这个输出:

{
    //...
    "createdDate": "2017-07-02T14:11:45.964+0200",
    //...
}

不幸的是,参数spring.jackson.time-zone(根据我的简短调查)仅影响应用程序中的服务消息,例如:

{
    "timestamp": "2017-07-02T14:14:09.486+0200",
    "status": 404,
    "error": "Not Found",
    "message": "No message available",
    "path": "/users"
}

要以“分区”形式在此处获取时间,我们必须在application.properties 中正确设置spring.jackson.date-format 参数:

spring.jackson.date-format=com.fasterxml.jackson.databind.util.StdDateFormat

【讨论】:

  • 我很确定。在数据库中,时间是 UTC,但是当我使用 Spring REST 返回值时,日期是我的本地时区。
  • 我就是这样:spring.jpa.hibernate.jdbc.time_zone = UTC 在你的问题中。在我的回答中,我建议使用另一个选项:'spring.jackson.time-zone'
  • 是的,我确定。这是我的属性文件的一部分:#Jackson spring.jackson.mapper.infer-property-mutators=false spring.jackson.time-zone=UTC
  • 您的 JDK 是否与 UTC 位于不同的时区?您是否在数据库中以 UTC 格式存储日期?
  • @drenda 现在没关系...我被错误消息中的时间值误导了。看我的回答我更新了...
猜你喜欢
  • 2016-05-21
  • 2019-11-18
  • 2018-04-24
  • 1970-01-01
  • 2017-10-27
  • 2021-08-23
  • 2021-02-06
  • 2021-02-21
  • 1970-01-01
相关资源
最近更新 更多