SATURN  RING  STATION

At a distant space view post, we chat about codes.

Learning deserialization of Date with timezone, Jackson, MyBatis, and MySQL. Why is it always one day early?

I ran into trouble deserializing json date to java date. When I POST an object with date to my RESTful api, it’s set back one day automatically.

 

At first, I suspended Jackson isn’t doing it the way I wanted. I took a good look into Jackson’s doc, they say they don’t really support java.sql.Date because it’s buggy. I changed to using java.util.Date; The problem remains that it shows one day earlier in system.out.print command line and in my database date column, but NOT in responsive body json.  After more search and read, I found out the problem around Date is way more complex than I initially thought.

 

 

When a json date comes in as: “2019-12-25”, Jackson have no way to know what timezone it’s in. The default design choice of Jackson is to set it like “2019-12-25 T 00:00:00” in UTC timezone, 1577232000000 (milliseconds after 1970/01/01 00:00:00 in UTC).

Let’s look at a demonstration, RESTful Controller:

    @RequestMapping(value = "/employees/{id}/titles", method = RequestMethod.PUT)
    public ResponseEntity<?> addEmployeeTitle(@PathVariable Integer id, @Valid @RequestBody Title newTitle){
        System.out.println(newTitle.getFromDate().getTime());
        System.out.println(newTitle.getToDate().getTime());

        ...
    }

Jackson annotations on domain POJO:

public class Title {

    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "PST")
    private Date fromDate;

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

}

fromDate is set to use PST.

toDate is default setting of Jackson, UTC.

 

Then we have:

Because of Jackson different timezone settings, the actual milliseconds are different.

 

The date changing problem occurs when I invokes the Java toString() method for Date object. It uses local host PST timezone, which changed Jackson default UTC timezone date to one day early as shown below:

It explains why it’s one day earlier in command line print-out. But what about in the database? Ha, same situation. MySQL took the “2019-12-25 T 00:00:00” in UTC timezone from MyBatis and converted it to its own system timezone, which is PST too.

 

 

Solution 1: Every layer/system at the back-end uses UTC. Only front-end/UI uses local timezone for user experience. Sounds the best, but do you have your dev computer set at UTC yourself? Hmm, not so easy.

 

Solution 2: Set timezone at MyBatis type handler level. Let me gladly redirect you to one of the great blog posts that talk about this problem: TimeZone handling with MyBatis and PostgreSQL

 

Solution 3: Set timezone at Jackson serialization/deserialization, best solution for me, cause we achieve timezone union at the back-end right after receiving the json date from the front-end. Just be careful that when you deploy your app, timezone settings may change depends on location and setting of the server!

import java.util.Date;

public class Title {

    ...

    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "PST")
    private Date fromDate;

    ...

}

 

Solution 4: Use String for date, not Date. Both Jackson and MyBatis support this parsing out of the box. Fit my need, but it’s situational. You did give-up handy functionality like before()/after().

 

2 Comments

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Learning deserialization of Date with timezone, Jackson, MyBatis, and MySQL. Why is it always one day early?