Search code examples
javagregorian-calendarjava.util.datejava.util.calendar

Why is my Java Calendar.setTime() sporadically setting wrong times?


Using this code below, I noticed that sometimes the date gets formatted incorrecty. And to make it even more weird, sometimes timeStamp will have the right date, and timeStampCopy will have the wrong date, and visa versa.

   public static Timestamp method(String date, DateFormat dateFormat) throws Exception {           

        // date is always "2017-02-17"     

        // original
        GregorianCalendar gCal = new GregorianCalendar();
        gCal.setTime(dateFormat.parse(date));
        Timestamp timeStamp = new Timestamp(gCal.getTimeInMillis());

        // copy
        GregorianCalendar gCalCopy= new GregorianCalendar();
        gCalCopy.setTime(dateFormat.parse(date));
        Timestamp timeStampCopy = new Timestamp(gCalCopy.getTimeInMillis());

        if (!timeStamp.toString().contains("2017-02-17"))
            System.out.println(timeStamp.toString());   
        if (!timeStampCopy.toString().contains("2017-02-17"))
            System.out.println(timeStampCopy.toString());   

        return timeStamp;

    }

I'm not sure what could be causing it but I tried this using a Date object and am having the same issues. I thought it could be a parsing issue but since it's doing the same thing twice I'm not sure.

Below are some of the values that I'm getting:

timeStamp is:       2017-02-17 00:00:00.0
timeStampCopy is:   1700-02-17 00:00:00.0

Solution

  • You say that you are sharing the DateFormat instance between threads.

    According to the Javadoc:

    Date formats are not synchronized. It is recommended to create separate format instances for each thread. If multiple threads access a format concurrently, it must be synchronized externally.

    Note that this refers to external synchronization of access to the DateFormat instance, not the method. Making the method synchronized would only fix this problem if there are no other uses of the DateFormat instance.

    You can either:

    • Explicitly synchronize around all code using the DateFormat instance (it is worth adding an @GuardedBy annotation to the variable, in order to document that you expect a lock to be held before using it);
    • Change the variable type to ThreadLocal<DateFormat> (and initialize the shared variable appropriately), which ensures that each thread has its own copy of the DateFormat.

    The latter approach has lower contention, because each thread can proceed independent of the others. It also means that you can't accidentally omit the synchronization.

    But, there are better libraries for handling dates and times, which were designed with the hindsight of problems like DateFormat's lack of thread safety. In Java 8, there is the java.time API; for earlier versions of Java, there is Jodatime.