technology from back to front

Parsing dates using a set format in Java SE

Tony mentioned a while back the parlous state of Java’s standard time libraries, and I wish I’d remembered before now, because he also pointed to Joda-time, which on the strength of a brief scan seems to me a compelling replacement. Hindsight, &c.

But to the matter at hand: getting a sensible result out of Java’s provisions for date parsing. Specifically, parsing a particular format, rather than a locale-dependent format. The stock way to do this is to use java.text.SimpleDateFormat.parse() with a suitable format string, say "dd MMM yyyy", which gives you a java.util.Date. The trouble starts when you want it to do what you mean; in my case, for 3 Oct 2007 to give you midnight on the 3rd of October 2007 UTC (there’s nothing in Java that represents an interval, so we have to settle for representative instants, and we can’t rely on things like databases to cope with timezones). Why doesn’t this work?

Because of the programmer’s friend the timezone. SimpleDateFormat assumes, if it’s not told, that what it’s parsing is in the system timezone, so in London the string from before is interpreted as midnight on the 3rd of October, British Summer Time; i.e., eleven at night on the 2nd, UTC. That’s not a bad assumption, although I didn’t see it documented anywhere, and fits well with the rest of the formatting APIs and locales and so on. The problem is that there’s no way to stop it behaving like that.

There are some teases, though: for example, java.text.DateFormat.setTimeZone() sounds like it will do what I want; but sadly, it only counts for formatting and not for parsing.

So, to the payoff: The expedient way to “neutralise” a date is to simply apply the system timezone offset and daylight savings offset to the parse result, then assert that it’s UTC.

import java.text.*;
import java.util.*;

public class UTCParse {
    public static void main(String[] args) {
    SimpleDateFormat sdf = new SimpleDateFormat("dd MMM yyyy");
 SimpleTimeZone utc = new SimpleTimeZone(0, "UTC");
  Calendar c = new GregorianCalendar(); // this picks up the system timezone
  try {
       Date d = sdf.parse(args[0]); // this assumes the system timezone
        c.setTimeInMillis(d.getTime());
     if (args.length > 1 && "utc".equals(args[1])) {
                // normalise the value
       c.setTimeInMillis(c.getTimeInMillis() +
                                        c.get(Calendar.ZONE_OFFSET) + 
                                        c.get(Calendar.DST_OFFSET));
       }

       SimpleDateFormat outformat = new SimpleDateFormat("dd MMM yyyy HH:mm Z");
       outformat.setTimeZone(utc);
     System.out.println(outformat.format(new Date(c.getTimeInMillis())));
    }
   catch (ParseException e) {
      System.out.println("Try a date formatted dd MMM yyyy");
 }
    }
}
by
mikeb
on
31/10/07
  1. scoutice
    on 04/03/08 at 1:48 pm

    thanks a lot for this valuable hint for me

  2. Another alternative would be:

    SimpleDateFormat timeFormatter = new SimpleDateFormat(“hh:mm:ss”);
    timeFormatter.setCalendar(Calendar.getInstance(TimeZone.getTimeZone(“UTC”)));
    System.out.println(timeFormatter.parse(“08:00:00.0000000″));

 
 


× 3 = twenty four

2000-14 LShift Ltd, 1st Floor, Hoxton Point, 6 Rufus Street, London, N1 6PE, UK+44 (0)20 7729 7060   Contact us