Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
988 views
in Technique[技术] by (71.8m points)

java - Spring REST LocalDate UTC differs of one day

I'm using Spring Boot 1.5.4, Hibernate 5.2.10, Spring Data REST, HATEOAS, JDK8 with LocalDate and LocalDateTime. My computer is on CEST timezone but I want the application works in UTC, so I set in application.properties:

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

According to this article I don't want to change the timezone of my JVM because seems not to be a best practice.

Spring Data REST exposes my repositories and I use Swagger2 to have a nice interface to use API. When I try a endpoint I see something like:

    {
  "_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"
          }
        }
      }

Like you can see the datetime format is fine and also it display the CEST time even if in the db the real time is 14:20:01. I guess this is wrong because my REST API should work in UTC.

How could I achieve this result?

Always on the same topic, I've a REST endpoint (exposed by Spring Data REST) to search using LocalDate params; I'm using

@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);
}

Also in this case I've a strange thing: if I call the endpoint passing parameters, in the server they arrive with 1 day less. Furthemore the date pattern accepted seems to be the JDK default of my locale (Italian) but I guess it's not a best practice.

Is there a best practice to follow to avoid any problems with date/time arguments in both direction?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I agree with the author of this article The 5 laws of API dates and times that we have to store and return time in UTC. And 'front-end' must decide itself how to convert the time value depending on the client time-zone.

To achieve this (store and return time in UTC) we set JVM parameter -Duser.timezone=UTC or add spring.jpa.properties.hibernate.jdbc.time_zone=UTC to application.properties (starting from Hibernate 5.2.3.Final). Then change time fields of our entities from LocalDateTime (that doesn't store time-zone info) to ZonedDateTime type.

After that the time values will be stored in UTC independently from the local time-zone of the computer where the application is started and SDR will return these values in ISO8601 form: 2017-07-02T11:58:10.089Z

But if it's necessary to return time values in the specific time-zone we have to setup @JsonFormat annotation to all of our time fields:

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

Our we can define constant TIME_ZONE in our application and set it in @JsonFormat annotations: @JsonFormat(timezone = Application.TIME_ZONE, ...).

Then we get this output:

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

Unfortunately, parameter spring.jackson.time-zone (based on my short investigation) affects only on service messages in the application, for example:

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

To get time here in 'zoned' form we have to set properly spring.jackson.date-format parameter in the application.properties:

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...