Search code examples
javalinuxdatecrondst

Using new java.util.Date() Returns An Hour Behind With DST


I have a Java program that runs on a linux virtual machine as a cron job. Currently there's an issue, the following line is used as part of logging to store the current date and time:

String date = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss").format(new Date());

After daylight savings today, it was determined the system date was not set up correctly as it did not update. After fixing this using the following command, and restarting the linux environment the date is now correct and updated for daylight savings time.

ln -s /usr/share/zoneinfo/America/New_York /etc/localtime

However, when the Java program executes as part of the cron job, the date is still an hour behind. This causes timing issues internally as new Date() is used to get the current system time, which causes later problems in the process. For example at 5:00 PM, the Java program outputs 4:00 PM. Is there an additional consideration to this implementation? Shouldn't new Date() always return the current system date?

Adding debugging lines to the cron job, this executes with the expected correct time.


Solution

  • tl;dr

    • Instant.now()
    • Update your JVM’s tzdata
    • Work in UTC, especially your logging
    • Serialize to ISO 8601 formats

    Date class

    Shouldn't new Date() always return the current system date?

    Yes, the Date class always captures the current moment in UTC. Let me repeat that: Date captures the moment in UTC.

    The trouble comes from a well-intentioned but unfortunate “feature” of that class’ toString method where the JVM’s current default time zone is applied in generating that string. One of many reasons to avoid this outmoded and troublesome class.

    I suspect that your “tzdata” time zone data file is outdated and not recognizing the new date for DST cutover.

    Restart JVM

    Java implementations typically pick up their current default time zone from the host OS when launching. After the JVM, changing the host OS’ time zone has no effect on the JVM.

    Restart the JVM to pick up a new zone setting in host OS.

    Use UTC on servers

    Best practice is usually to set your servers to UTC. Then you do not need to worry about Daylight Saving Time nonsense.

    Work in UTC

    The bulk of your programming, logging, cron jobs, data exchange, and data serialization should be in UTC.

    Note, for example, how Stack Overflow uses UTC in reporting your activity for "today" and "yesterday". Learn to think of UTC as The One True Time; all zones are a variation on that theme.

    Apply a time zone only where critical, such as in presentation to a user expecting a certain zone.

    Remember that your problem was not due to the bending of space-time. The seconds, minutes, and hours continued to increment normally, tick-tock tick-tock, in terms of UTC. Your problem was in the mistranslation into a time zone with outdated time zone rule change data.

    Specify your desired zone in Java code

    Your Java apps should always specify their desired/intended time zone explicitly. If omitted, the JVM’s current default time zone is implicitly applied.

    That JVM default can be changed at any time by any code in any thread of any app within the JVM and immediately affect all other code in that JVM. So never depend on that current default for anything important. Confirm with the user, and specify explicitly in your code.

    Avoid legacy date-time classes

    The old date time classes including Date and Calendar are a bloody mess. Avoid them. They are now legacy, supplanted by the java.time classes.

    Instant class is a moment on the time line with a resolution in nanoseconds, always in UTC.

    Instant instant = Instant.now() ;
    

    To generate a standard ISO 8601 formatted string, call toString. The Z on the end is short for Zulu and means UTC.

    String output = instant.toString() ;
    

    2017-01-23T12:34:56.123456789Z

    Using those two simple lines of code would avoid your entire problem.

    But you should be routinely updating the tzdata in:

    • host OS
    • JVM
    • Database system (Postgres, etc.)
    • Libraries such as Joda-Time