The Unparseable Date Pitfall

Here is another pitfall with the SimpleDateFormat class.
Unlike my previous blog entry, this pitfall is not a multithreading issue; the following code is thread-safe. However, it can fail in certain situations.

package com.udojava.blog;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class ParseDateDemo {

	public static void main(String[] args) throws ParseException {
		String dateString = "Thu Oct 25 6:12 pm PDT 2012";

		SimpleDateFormat dateFormat = new SimpleDateFormat(
				"EEE MMM d h:mm a z yyyy");

		Date theDate = dateFormat.parse(dateString);

		System.out.println(dateFormat.format(theDate));
	}

}

The code will run on a lot of systems without ever failing, but on certain system setups, it will fail.

For example, on my computer it won’t run and instead produce the following exception:

Exception in thread "main" java.text.ParseException: Unparseable date: "Thu Oct 25 6:12 pm PST 2012"
at java.text.DateFormat.parse(DateFormat.java:357)
at com.udojava.blog.ParseDateDemo.main(ParseDateDemo.java:15)

This is simply because my system is set up with a German locale setting, which does not understand the English names for the day (Thu) and month (Oct). The code will fail on all systems, which do not run under an English locale setup.

You can test it, by giving the Java arguments –Duser.country=DE –Duser.language=de when starting the program. This will set the Java default locale to German/Germany.

Now how to fix it? Starting the program with –Duser.language=en and forcing the users to use your locale is certainly not a good way to go.

Luckily, we can take care that the SimpleDateFormat class works with the correct locale, simply by adding a locale parameter to the constructor:

		SimpleDateFormat dateFormat = new SimpleDateFormat(
				"EEE MMM d h:mm a z yyyy", Locale.ENGLISH);

Now the program will run correctly, no matter what default locale Java is using.

The “write once, run anywhere” slogan from Java needs most of the time more care by the programmer than just using the Java programming language.

Advertisement

Java SimpleDateFormat gives wrong results

Would you expect the following lines of code to ever print something to the standard output?

public static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
...
Date date = dateFormat.parse("2012-10-26");
String reformat = dateFormat.format(date);
if (!"2012-10-26".equals(reformat)) {
  System.out.println("Wrong date: " + reformat);
  }

Indeed, there are situations when such code will occasionally fail.
The SimpleDateFormat class is not thread safe, that means whenever two or more separate threads access the same class instance, the results from the parse() and format() methods can produce wrong results.
To reproduce this effect, I show the full test code:

package com.udojava.blog;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class DateFormatDemo {

	public static SimpleDateFormat dateFormat = new SimpleDateFormat(
			"yyyy-MM-dd");

	public class DemoThread extends Thread {

		@Override
		public void run() {
			for (int i = 0; i < 100; i++) {
				try {
					Date date = dateFormat.parse("2012-10-26");
					String reformat = dateFormat.format(date);
					if (!"2012-10-26".equals(reformat)) {
						System.out.println("Wrong date: " + reformat);
					}
				} catch (ParseException e) {
					e.printStackTrace();
				}
			}
		}

	}

	public void testRun() {
		DemoThread t1 = new DemoThread();
		DemoThread t2 = new DemoThread();
		t1.start();
		t2.start();
	}

	public static void main(String[] args) {
		new DateFormatDemo().testRun();
	}

}

If you run this, you will get differing outputs from one run to the next run, something like this:

Wrong date: 2201-10-26
Wrong date: 2201-10-26
Wrong date: 2012-12-26
Exception in thread "Thread-1" java.lang.NumberFormatException: empty String
at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1011)
at java.lang.Double.parseDouble(Double.java:540)
at java.text.DigitList.getDouble(DigitList.java:168)
at java.text.DecimalFormat.parse(DecimalFormat.java:1321)
at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:2088)
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1455)
at java.text.DateFormat.parse(DateFormat.java:355)
at com.udojava.blog.DateFormatDemo$DemoThread.run(DateFormatDemo.java:18)

So, sometimes it can be dangerous to try to over-optimize by using static members that are shared between threads. Even simple things like a SimpleDateFormat can fail in multithreading.
And they usually then fail only in 1 out of 1000 times, making it a real horror to find, debug and fix those issues.

%d bloggers like this: