compliant String to java.util.Date

I am trying to convert an ISO 8601 formatted String to a java.util.Date.

I found the pattern "yyyy-MM-dd'T'HH:mm:ssZ" to be ISO8601-compliant if used with a Locale (compare sample). However, using the java.text.SimpleDateFormat, I cannot convert the correctly formatted String "2010-01-01T12:00:00+01:00". I have to convert it first to "2010-01-01T12:00:00+0100", without the colon. So, the current solution is

SimpleDateFormat ISO8601DATEFORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.GERMANY);
String date = "2010-01-01T12:00:00+01:00".replaceAll("+0([0-9]){1}:00", "+0$100");
System.out.println(ISO8601DATEFORMAT.parse(date));

which obviously isn't that nice. Am I missing something or is there a better solution?

answer

Thanks to JuanZe's comment, I found the Joda-Time magic, it is also described here. So, the solution is

DateTimeFormatter parser2 = ISODateTimeFormat.dateTimeNoMillis();
String jtdate = "2010-01-01T12:00:00+01:00";
System.out.println(parser2.parseDateTime(jtdate));

Or more simply, use the default parser via the constructor:

DateTime dt = new DateTime( "2010-01-01T12:00:00+01:00" ) ;

To me, this is nice.


Unfortunately, the time zone formats available to SimpleDateFormat (Java 6 and earlier) are not ISO 8601 compliant. SimpleDateFormat understands time zone strings like "GMT+01:00" or "+0100", the latter according to RFC # 822.

Even if Java 7 added support for time zone descriptors according to ISO 8601, SimpleDateFormat is still not able to properly parse a complete date string, as it has no support for optional parts.

Reformatting your input string using regexp is certainly one possibility, but the replacement rules are not as simple as in your question:

  • Some time zones are not full hours off UTC, so the string does not necessarily end with ":00".
  • ISO8601 allows only the number of hours to be included in the time zone, so "+01" is equivalent to "+01:00"
  • ISO8601 allows the usage of "Z" to indicate UTC instead of "+00:00".
  • The easier solution is possibly to use the data type converter in JAXB, since JAXB must be able to parse ISO8601 date string according to the XML Schema specification. javax.xml.bind.DatatypeConverter.parseDateTime("2010-01-01T12:00:00Z") will give you a Calendar object and you can simply use getTime() on it, if you need a Date object.

    You could probably use Joda-Time as well, but I don't know why you should bother with that.


    Okay, this question is already answered, but I'll drop my answer anyway. It might help someone.

    I've been looking for a solution for Android (API 7).

  • Joda was out of the question - it is huge and suffers from slow initialization. It also seemed a major overkill for that particular purpose.
  • Answers involving javax.xml won't work on Android API 7.
  • Ended up implementing this simple class. It covers only the most common form of ISO 8601 strings, but this should be enough in some cases (when you're quite sure that the input will be in this format).

    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.Calendar;
    import java.util.Date;
    import java.util.GregorianCalendar;
    
    /**
     * Helper class for handling a most common subset of ISO 8601 strings
     * (in the following format: "2008-03-01T13:00:00+01:00"). It supports
     * parsing the "Z" timezone, but many other less-used features are
     * missing.
     */
    public final class ISO8601 {
        /** Transform Calendar to ISO 8601 string. */
        public static String fromCalendar(final Calendar calendar) {
            Date date = calendar.getTime();
            String formatted = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
                .format(date);
            return formatted.substring(0, 22) + ":" + formatted.substring(22);
        }
    
        /** Get current date and time formatted as ISO 8601 string. */
        public static String now() {
            return fromCalendar(GregorianCalendar.getInstance());
        }
    
        /** Transform ISO 8601 string to Calendar. */
        public static Calendar toCalendar(final String iso8601string)
                throws ParseException {
            Calendar calendar = GregorianCalendar.getInstance();
            String s = iso8601string.replace("Z", "+00:00");
            try {
                s = s.substring(0, 22) + s.substring(23);  // to get rid of the ":"
            } catch (IndexOutOfBoundsException e) {
                throw new ParseException("Invalid length", 0);
            }
            Date date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").parse(s);
            calendar.setTime(date);
            return calendar;
        }
    }
    

    Performance note: I instantiate new SimpleDateFormat every time as means to avoid a bug in Android 2.1. If you're as astonished as I was, see this riddle. For other Java engines, you may cache the instance in a private static field (using ThreadLocal, to be thread safe).


    The way that is blessed by Java 7 documentation:

    DateFormat df1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
    String string1 = "2001-07-04T12:08:56.235-0700";
    Date result1 = df1.parse(string1);
    
    DateFormat df2 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
    String string2 = "2001-07-04T12:08:56.235-07:00";
    Date result2 = df2.parse(string2);
    

    You can find more examples in section Examples at SimpleDateFormat javadoc.

    链接地址: http://www.djcxy.com/p/35110.html

    上一篇: 将日期转换为ISO 8601

    下一篇: 兼容的String到java.util.Date