0

I having issues when Jackson tries to serialize the LocalDate columns from my classes but the database is completely fine, everytime the JSON displays one day off from the database.

While the database display as intended:

2018-10-01

The JSON output from calling a Spring REST entrypoint:

{ "ticketStart": "2018-09-30" }

It shows an day was lost. The database table doesn't store the time, just the date as intended, The timezones are completely discardable, since my project is for internal use. I tried to google my specific issue and the only i found this and this, but none of them solved my problem.

There's a way to enforce to serialize the dates from my database as-is, (aka: conversion disabled).

Entity Code:

@Entity
@Table(name="tickets")
public class Ticket
{
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Getter
  @Setter
  private long TicketId;

  @Column(unique=true)
  @Getter
  @Setter
  private LocalDate TicketStart;

  @Column(unique=true)
  @Getter
  @Setter
  private LocalDate TicketEnd;

  //.... some fields omitted
}

Since the Tickets entity are generated automatically by a CRON script or specified in a range of dates, where a ticket has the Monday and Sunday of every week:

public Ticket GenerateEmptyTicket(LocalDate begin, LocalDate end)
{
    Ticket newTicket = new Ticket();

    newTicket.setTicketStart(begin);
    newTicket.setTicketEnd(end);
    newTicket.setYear(begin.getYear());
    newTicket.setMonth(begin.getMonth());
    newTicket.setLastMod(new Date());

    boolean isLocked = false;
    LocalDate date = LocalDate.now();
    if(date.isAfter(end)) isLocked = true;

    newTicket.setLocked(isLocked);

    //@TODO: Set a default responsible in the configuration.
    SystemUser rootUser = usersRepo.findByUsername("root").get();
    newTicket.setResponsable(rootUser);

    return newTicket;
}

public void GenerateTicketsBetweenTwoDates(LocalDate begin, LocalDate end)
{        
    List<LocalDate> weekDates = new ArrayList<>();

    if(begin.getDayOfWeek() != DayOfWeek.MONDAY)
        while(begin.getDayOfWeek()  != DayOfWeek.MONDAY)
            begin = begin.minusDays(1);


    LocalDate tmp = begin;
    while(tmp.isBefore(end)){
        weekDates.add(tmp);
        tmp = tmp.plusWeeks(1);
    }

    if(end.getDayOfWeek() == DayOfWeek.MONDAY)
        weekDates.add(end);


    LocalDate current = LocalDate.now();
    List<Ticket> ticketsToSave = new ArrayList<>();

    for(LocalDate dat : weekDates)
    {
        logger.info("BEGIN DATE:" + dat.toString());
        LocalDate __end = dat.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY));

        if(current.isAfter(__end)) logger.info("LOCKED: YES");

        ticketsToSave.add(GenerateEmptyTicket(dat, __end));
    }

    ticketsRepo.saveAll(ticketsToSave);
}    

The database and the Jackson serialization displays no errors, just the undesired "day off" result from Serialization.

NOTE: The database JDBC connection (MySQL) specifies useJDBCCompliantTimezoneShift=false&useLegacyDatetimeCode=false&serverTimezone=UTC from the application.properties spring.datasource.url

Cristopher Sosa
  • 167
  • 3
  • 15
  • 1
    Please include the code you use. Have you already used a debugger to determine if the error is introduced when the date is read from the DB, or when it's serialized into JSON? – Joni Dec 11 '18 at 00:20
  • @Joni the error isn't introduced in the DB, as i mentioned before, the date added from Java to the database shows the value as intended, but once displayed from JSON results shows the implicit conversion once i do an GET call, Spring doesn't display anything in the logs/debug output. – Cristopher Sosa Dec 11 '18 at 00:30
  • What you get if you debug your object before deserialisation ? – Abder KRIMA Dec 11 '18 at 00:36
  • What a funny thing, i just did that and retrieving the object from the database in Java it shows the side-effect conversion of the day off – Cristopher Sosa Dec 11 '18 at 00:43
  • Could you add on top of your LodalDate fields : @JsonDeserialize(using = LocalDateDeserializer.class) @JsonSerialize(using = LocalDateSerializer.class) and try again if it will work ! – Abder KRIMA Dec 11 '18 at 01:08
  • @TinyOS, i did it, and didn't work, still the same result. – Cristopher Sosa Dec 11 '18 at 02:01
  • By the way you get a day off during deserialisation or serialisation ? – Abder KRIMA Dec 11 '18 at 02:32
  • @TinyOS both, unfortunately. – Cristopher Sosa Dec 11 '18 at 03:19
  • By all likelihood it’s a time zone issue. – Ole V.V. Dec 11 '18 at 09:18
  • As an aside, to set a date back to Monday of the same week just use `begin = begin.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY);`. No loop or `if` statement needed. The list may be formed with `begin.datesUntil(end, Period.ofWeeks(1)).collect(Collectors.toList())`. – Ole V.V. Dec 11 '18 at 09:20
  • Well, i don't know what to do now, i tried everything and nothing, only my workaround is before sending the data to the client, i have to add one day more to the selected entity to send. Also, seems likely when i try to convert my date to client JS using toLocaleDateString() also the effect of the day off applies. – Cristopher Sosa Dec 16 '18 at 00:55

1 Answers1

2

Timezone issue

As much Turns out, Spring retrieves the dates from the database and i guess it uses java.util.Date (which considers system's timezone) to parse it first and later convert it to java.time.LocalDate, using TimeZone.setDefault(TimeZone.getTimeZone("UTC")); completely fix the issue, using it like this:

@SpringBootApplication
public class MyTicketApplication {

    public static void main(String[] args) {
        TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
        SpringApplication.run(MyTicketApplication.class, args);
    }
}
Cristopher Sosa
  • 167
  • 3
  • 15
  • I added that line in the entityManagerFactory() bean where other persistence settings are defined, and it solved my problem application-wide. // Timezone for database dates TimeZone.setDefault(TimeZone.getTimeZone("America/Denver")); This matches how the database URL is defined: jdbc:mysql://localhost:3306/db?useSSL=false&serverTimezone=America/Denver – Jubz Jan 15 '20 at 10:05