Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SimpleDateFormat - not safe but why exactly? [duplicate]

I recently realized/learned that SimpleDateFormat has some serious issues and since Java 8 should not be used anymore. I mean... I kind of knew it but never payed much attention to that. So far so good.

But then OK... I have lots of legacy code written in the last 7-8 years which does use SimpleDateFormat, stores many SimpleDateFormat objects as static fields, and uses them to parse/format dates. And actually I've never had any issues with these SimpleDateFormat instances (static or not) in production (for all these years).

So... I want to now review and analyze this legacy code and see if there are really any dangerous uses of SimpleDateFormat in it.

Therefore my question is...

Under what scenarios exactly is SimpleDateFormat problematic to use?
Can I get some sort of checklist so that I review my old code and see if any of my scenarios are in that "try to avoid" list?

like image 348
peter.petrov Avatar asked Oct 23 '25 17:10

peter.petrov


2 Answers

SimpleDateFormat is not threadsafe, any scenario where you put it in a field accessed by multiple threads is going to be a potential problem. It won't blow up on you, but it can cause incorrect results to be generated.

Triaging cases of this would be a matter of checking what you're using the results for and how important it is that those values are always correct. Also obviously the more threads you have hitting a formatter the greater the chance of errors, so applications that are multithreaded but which rarely have more than one user might be prioritized downward.

like image 152
Nathan Hughes Avatar answered Oct 26 '25 07:10

Nathan Hughes


One problem, for example, is that SimpleDateFormat has an internal field that is set to the date/calendar you want to format.

So if you have two threads using the same SDF concurrently with two different dates, the date being formatted may change right in the middle of the formatting, resulting in a String that is a mix between the two dates.

That is what the example below simulates. If you run it with ExecutorService es = Executors.newFixedThreadPool(1); (single threaded), the resulting set only has two dates, as expected. If you use ExecutorService es = Executors.newFixedThreadPool(10); instead (multi threaded), it is likely that the resulting set will have more dates, which are a mix of the two dates.

For example, on my machine the outputs are:

  • single threaded (expected result):

    [02-Jan-1970 04:46:40, 01-Jan-1970 01:00:00]

  • multi threaded:

    [02-Jan-1970 01:00:00, 01-Jan-1970 04:00:00, 02-Jan-1970 01:00:40, 01-Jan-1970 01:00:40, 02-Jan-1970 04:46:00, 02-Jan-1970 01:46:00, 01-Jan-1970 01:00:00, 02-Jan-1970 04:00:00, 02-Jan-1970 01:46:40, 02-Jan-1970 04:00:40, 01-Jan-1970 01:46:00, 01-Jan-1970 01:46:40, 02-Jan-1970 04:46:40, 01-Jan-1970 04:00:40, 01-Jan-1970 04:46:40, 01-Jan-1970 04:46:00]


private static final DateFormat FMT = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss");
private static final CountDownLatch LATCH = new CountDownLatch(1);

public static void main(String[] args) throws Exception {
  ExecutorService es = Executors.newFixedThreadPool(1);
  Date d1 = new Date(0);
  Date d2 = new Date(100_000_000);
  List<Future<String>> futures = new ArrayList<> ();
  for (int i = 0; i < 10_000; i++) {
    Date d = i % 2 == 0 ? d1 : d2;
    Future<String> f = es.submit(() -> run(d));
    futures.add(f);
  }
  LATCH.countDown();
  es.shutdown();
  es.awaitTermination(5, TimeUnit.SECONDS);
  Set<String> results = new HashSet<> ();
  for (Future<String> f : futures) {
    results.add(f.get());
  }

  System.out.println(results);
}

private static String run(Date d) throws InterruptedException {
  LATCH.await();
  return FMT.format(d);
}
like image 43
assylias Avatar answered Oct 26 '25 07:10

assylias



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!