Problem with timezone in Json response from Spring

前端 未结 1 857
日久生厌
日久生厌 2020-12-21 18:25

I have a problem with displaying dates in JSON output. In code I use java.util.Date and its value is 2019-03-07 but in JSON I got 2019-03-06

相关标签:
1条回答
  • 2020-12-21 19:00

    Date is outdated class and should not be used since Java 8 released java.time package or we can use Joda-Time. You are converting date from Timestamp to java.sql.Date and later to java.util.Date. This is very unsafe, see below example:

    import com.fasterxml.jackson.annotation.JsonFormat;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.SerializationFeature;
    
    import java.time.LocalDate;
    import java.time.ZoneId;
    import java.util.Calendar;
    import java.util.Date;
    import java.util.TimeZone;
    
    public class JsonApp {
    
        public static void main(String[] args) throws Exception {
            ObjectMapper mapper = new ObjectMapper();
            mapper.enable(SerializationFeature.INDENT_OUTPUT);
    
            // Java time precise dates
            LocalDate localDateOpened = LocalDate.of(2019, 03, 07);
            LocalDate localDateClosed = localDateOpened.plusDays(20);
    
            ZoneId utc = ZoneId.of("UTC");
            Date opened = Date.from(localDateOpened.atStartOfDay(utc).toInstant());
            Date closed = Date.from(localDateClosed.atStartOfDay(utc).toInstant());
    
            System.out.println("Dates generated from java.time.*");
            System.out.println(mapper.writeValueAsString(new ThisDay(opened, closed)));
    
            // Calculate dates with default timezone
            Calendar calendar = Calendar.getInstance();
            opened = calendar.getTime();
            calendar.add(Calendar.DAY_OF_MONTH, 20);
            closed = calendar.getTime();
    
            System.out.println("Dates generated from Calendar");
            System.out.println(mapper.writeValueAsString(new ThisDay(opened, closed)));
    
            // Calculate dates with UTC timezone
            calendar = Calendar.getInstance();
            calendar.setTimeZone(TimeZone.getTimeZone(utc));
            calendar.set(Calendar.MILLISECOND, 0); // Recompute
    
            opened = calendar.getTime();
            calendar.add(Calendar.DAY_OF_MONTH, 20);
            closed = calendar.getTime();
    
            System.out.println("Dates generated from UTC Calendar");
            System.out.println(mapper.writeValueAsString(new ThisDay(opened, closed)));
        }
    }
    
    class ThisDay {
    
        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
        private Date opened;
    
        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
        private Date closed;
    
        public ThisDay(Date opened, Date closed) {
            this.opened = opened;
            this.closed = closed;
        }
    
        public Date getOpened() {
            return opened;
        }
    
        public void setOpened(Date opened) {
            this.opened = opened;
        }
    
        public Date getClosed() {
            return closed;
        }
    
        public void setClosed(Date closed) {
            this.closed = closed;
        }
    }
    

    Above code prints:

    Dates generated from java.time.*
    {
      "opened" : "2019-03-07 00:00:00",
      "closed" : "2019-03-27 00:00:00"
    }
    Dates generated from Calendar
    {
      "opened" : "2019-03-27 23:45:12",
      "closed" : "2019-04-16 22:45:12"
    }
    Dates generated from UTC Calendar
    {
      "opened" : "2019-03-28 00:45:12",
      "closed" : "2019-04-17 00:45:12"
    }
    

    Notice that second and third opened dates has difference one hour. I manually set calendar timezone to UTC and force to recompute values setting milliseconds to 0:

    calendar.setTimeZone(TimeZone.getTimeZone(utc));
    calendar.set(Calendar.MILLISECOND, 0); // Recompute
    

    This is why Date is outdated and java.time package should be used. If you do not want to show time, only date - change format to @JsonFormat(pattern = "yyyy-MM-dd").

    See also:

    • Spring Boot Jackson date and timestamp Format
    0 讨论(0)
提交回复
热议问题