Search code examples
springhibernatejpathymeleaflocaldate

Wrong LocalDate stored with hibernate


I'm trying to store inside the database the date of a restaurant booking but, even though the date I submit is correct, hibernate stores inside the database a date one day before the one I submitted. I don't know why... it's probably a timezone problem but I can't understand why... the date should not be affected by the timezones.

Here is my spring boot properties file:

spring:
  thymeleaf:
    mode: HTML5
    encoding: UTF-8
    cache: false
  jpa:
    database: MYSQL
    hibernate:
      ddl-auto: update
    properties:
      hibernate:
        locationId:
          new_generator_mappings: false
        dialect: org.hibernate.dialect.MySQL5InnoDBDialect
        jdbc:
          time_zone: UTC
  datasource:
    driver:
      class: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/databaseName?useSSL=false&useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
    username: username
    password: **********

I'm from Italy, so my timezone is this:

  • GMT/UTC + 1h during Standard Time
  • GMT/UTC + 2h during Daylight Saving Time

Currently we are UTC + 2h.

The object I'm storing is this one:

@Entity
public class Dinner {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long dinnerId;

    private LocalDate date;
    ...

The controller I'm using to intercept the POST request is this:

@PreAuthorize("hasRole('USER')")
@PostMapping
public String createDinner(@RequestParam(value="dinnerDate") String dinnerDate, Principal principal, Model model){
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        LocalDate date = LocalDate.parse(dinnerDate, formatter);
        dinnerService.createDinner(date);
        return "redirect:/dinners?dinnerDate=" + dinnerDate;
}

Which calls the service method createDinner that call the Jpa method save to store the object. I'm using thymeleaf to handle the html templates. If I submit the date 30/6/2019 inside the database I get 29/6/2019. When I retrieve the Dinner object by date, if I insert 30/6/2019, I get the Dinner with the date 29/6/2019. So it seems that spring handle the date by itself in a weird way... considering some sort of timezone but I don't know how to disable or handle it. Any idea?


Solution

    1. You do not need to define a format for the pattern yyyy-MM-dd. LocalDate#parse uses DateTimeFormatter.ISO_LOCAL_DATE by default which means LocalDate.parse("2020-06-29") works without applying a format explicitly.

    2. Since you already know that date-time in your time-zone is different from that in UTC, you should never consider just a date; rather you should consider both date and time e.g. 11:30 PM at UTC on 2020-06-29 will fall on 2020-06-30 in your time-zone. Therefore, the first thing you should do is to change the type of the field as TIMESTAMP in the database.

    3. Once you have changed the type of the field to TIMESTAMP, change the method, createDinner as follows:

      LocalDateTime dinnerDateTime = LocalDateTime.of(LocalDate.parse(dinnerDate), LocalTime.of(0, 0, 0, 0));
      OffsetDateTime odt = dinnerDateTime.atOffset(ZoneOffset.UTC);
      dinnerService.createDinner(odt);
      

      Then inside DinnerService (or DinnerServiceDAO wherever you have written the logic to insert/update record in the database):

      pst.setObject(index, odt);
      

      where pst represents the object of PreparedStatement and index represents the index (starting with 1) of this field in your insert/update query.