tags:

views:

108

answers:

4

I'm using java.text.SimpleDateFormat to parse string representations of date/time values inside an XML document. I'm seeing all times that have an hour value of 12 shifted by 12 hours into the future, i. e. 20 minutes past noon gets parsed to mean 20 minutes past midnight the following day.

I wrote a unit test which seems to confirm that the error is made upon parsing (I checked the return values from getTime() with the linux shell command date). Now I'm wondering:

  • is there a bug in the parse() method?
  • is there something wrong with the input string?
  • am I using the wrong format string for the input?

The input data is taken from Yahoo's YWeather service. Here's the test and its output:

public class YWeatherReaderTest
{
    public static final String[] rgDateSamples = {
        "Thu, 08 Apr 2010 12:20 PM CEST",
        "Thu, 08 Apr 2010 12:20 AM CEST"
    };

    public void dateParsing() throws ParseException
    {
        DateFormat formatter = new SimpleDateFormat("EEE, dd MMM yyyy K:m a z",
                                                    Locale.US);
        for (String dtsSrc : YWeatherReaderTest.rgDateSamples) {
            Date dt = formatter.parse(dtsSrc);
            String dtsDst = formatter.format(dt);
            System.out.println(dtsSrc);
            System.out.println(dtsDst);
            System.out.println();
        }
    }
}
Thu, 08 Apr 2010 12:20 PM CEST
Fri, 09 Apr 2010 0:20 AM CEST

Thu, 08 Apr 2010 12:20 AM CEST
Thu, 08 Apr 2010 0:20 PM CEST

The second output line of the second iteration is slightly weird, because 00:20 isn't PM. The milliseconds value of the Date object, however, corresponds to the (wrong) time of 20 minutes past noon.

+3  A: 

If you use K in the format string, it goes from 0-11 (so 12:20 should really be 00:20). You can try using h instead, that goes from 1-12, which is what you were expecting.

http://java.sun.com/j2se/1.4.2/docs/api/java/text/SimpleDateFormat.html

muddybruin
Thanks. Your answer is essentially the same as bobince's, but his is (very) slightly more precise, so I accepted that one and upvoted yours. Hope that's OK.
Hanno Fietz
+3  A: 

The K specifier in SimpleDateFormat is documented to use hours starting from 0. Not sure what's supposed to happen if you ask it to parse an out-of-range value like 12... it's probably acting as if you input 00:20 PM and then adding on an extra 12 hours.

If you want to use 12 for the first hour, try the h specifier instead.

Why won't the horror of the broken 12-hour clock system die already?

bobince
Yes, why won't it? Anyway, this helped.
Hanno Fietz
+1  A: 

If you use K .... etc. etc. ... ( read bobince's answer )

To force the expected behavior ( and throw a parse exception when using 12 ) set the parser's lenient property to false:

....
DateFormat formatter = new SimpleDateFormat("EEE, dd MMM yyyy K:m a z",
                                                Locale.US);
formatter.setLenient( false );
for (String dtsSrc : YWeatherReaderTest.rgDateSamples) {
....

Output:

java YWeatherReaderTest
Exception in thread "main" java.text.ParseException: Unparseable date: "Thu, 08 Apr 2010 12:20 PM CEST"
at java.text.DateFormat.parse(DateFormat.java:335)
at YWeatherReaderTest.dateParsing(YWeatherReaderTest.java:17)
at YWeatherReaderTest.main(YWeatherReaderTest.java:25)
OscarRyz
That's good to know.
Hanno Fietz
A: 

As a side mark: I'd strongly suggest to switch to Joda Time.

The java.util.date functions tend to just guess (which in your case was not what you expected). Joda Time throws an exception if it's not according to the specs.

This is close to what you have:

String s = "Thu, 08 Apr 2010 12:20 PM";
String format = "EEE, dd MMM yyyy h:m a";
DateTimeFormatter fmt = DateTimeFormat.forPattern(format).withLocale(Locale.US);
System.out.println(fmt.parseDateTime(s));

Although Joda Time seems to not yet support the parsing of the time zone (see http://joda-time.sourceforge.net/api-release/org/joda/time/format/DateTimeFormat.html)

Philipp Keller