1

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 23:00:00. I think the problem is in timezone, but I don't use timezones in DB and in code too.

I was trying to fix it with

@JsonFormat(pattern = "yyyy-MM-dd hh:mm:ss", timezone="UTC")

and

@JsonFormat(pattern = "yyyy-MM-dd hh:mm:ss", timezone="Europe/Warsaw")

The first didn't help, the second helped but I don't accept this solution.

Part of my controller:

return new ThisDay(
    sysoperMgr.getToday(),
    new Date()
);

This is object which I return.

@Getter
@Setter
public class ThisDay {

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    Date dataZamkniecia;

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    Date dataSystemowa;

    public BiezacaDoba(Date dataZamkniecia, Date dataSystemowa) {
        this.dataZamkniecia = dataZamkniecia;  // cdate = 2019-03-07T00:00:00.000+0100
        this.dataSystemowa = dataSystemowa; // cdate = 2019-03-27T16:08:12.343+0100
    }
}

This function gets date:

public Date getToday() {

    Timestamp timestamp = sysoperDao.getDataOstatniejZamknietejDoby(); // cdate = 2019-03-06T00:00:00.000+0100
    java.util.Date lastDay = new java.sql.Date(misc.roundTimestamp(timestamp).getTime()); // cdate = 2019-03-06T00:00:00.000+0100
    java.util.Date thisDay = misc.incrementDate(ostatniaDoba, Increment.DAILY, 1); // cdate = 2019-03-07T00:00:00.000+0100
    return thisDay;
}

Json result:

{
  "dataZamkniecia":"2019-03-06 23:00:00",
  "dataSystemowa": "2019-03-27 15:12:15"
}

How do yo get the JSON to display the date always in the local timezone?

PaulNUK
  • 4,774
  • 2
  • 30
  • 58
Olek
  • 319
  • 8
  • 24
  • Jackson does use the local time zone unless configured otherwise. Try it with a small standalone program. Your issue comes from whatever surrounds the small part you're talking about. You'll need to give details about your application in its entirety, and the frameworks you're using. – kumesana Mar 27 '19 at 15:49
  • Is there a reason why you've chosen the legacy `java.util.Date` type? It is long since replaced by the `java.time.*` types. https://programminghints.com/2017/05/still-using-java-util-date-dont/ – Matt Johnson-Pint Mar 27 '19 at 17:13
  • @MattJohnson I use java.util.Date because in entities there are Dates from java.sql and converson between java.util.Date and java.sql.Date is simple. The second argument is that in my database dates are saved without timezone and I don't think that adding this function in my DB (Firebird) is now possible. Do you think in this situation I should use java.time.* instead java.util.Date?? What with entities, as I know JDBC uses java.sql.Date? – Olek Mar 30 '19 at 14:59

1 Answers1

4

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:

Michał Ziober
  • 37,175
  • 18
  • 99
  • 146