1

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
Overclock
  • 21
  • 1
  • 10
  • what are your inputs for function – Mithilesh Gupta Feb 17 '17 at 21:49
  • 4
    Are you sharing the `DateFormat` instance between threads? – Andy Turner Feb 17 '17 at 21:58
  • @AndyTurner Yes! I was just about to add that for some reason when I added `synchronized` to the method it seemed to suddenly start working properly. Not sure why though. – Overclock Feb 17 '17 at 22:10
  • 2
    I cannot more strongly advise against using the legacy `java.util.Calendar` and `java.util.Date` classes. You should seriously consider using the class in the `java.time` package most appropriate for your use case. – Joe C Feb 17 '17 at 22:12
  • 2
    It's not about making *this* method synchronized, it's about ensuring exclusive access to the `dateFormat`. Making this method synchronized might help, for now, but only until you start using that `DateFormat` somewhere else. – Andy Turner Feb 17 '17 at 22:12

2 Answers2

4

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.

Andy Turner
  • 137,514
  • 11
  • 162
  • 243
  • All good but that last sentence. The Joda-Time project is now in maintenance mode and recommends migration to the java.time classes. For earlier versions of Java (6 & 7), use the [ThreeTen-Backport](http://www.threeten.org/threetenbp/) project. Much of the java.time functionality is back-ported there. Further adapted to Android in the [ThreeTenABP](https://github.com/JakeWharton/ThreeTenABP) project. – Basil Bourque Feb 18 '17 at 18:22
1

The Answer by Turner is correct and should be accepted.

java.time is thread-safe

The java.time classes solve this problem by using immutable objects and making them inherently thread-safe.

LocalDate ld = LocalDate.of( "2017-02-17" );
ZoneId z = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ld.atStartOfDay( z );

Generate a string in standard ISO 8601 format by calling toString. For other formats, use DateTimeFormatter class. Search Stack Overflow for many examples and discussions. No worries about threads, all thread-safe.

For a value in UTC, extract an Instant.

Instant instant = zdt.toInstant() ;

No need to use java.sql.Timestamp. Modern JDBC drivers can handle the java.time types via toObject and setObject methods. For older drivers convert using new methods added to the old classes.


About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

Where to obtain the java.time classes?

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

Community
  • 1
  • 1
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154