我正在尝试将ISO 8601格式的字符串转换为java.util.Date。

我发现模式yyyy-MM-dd'T'HH:mm:ssZ是符合iso8601的,如果使用区域设置(比较样本)。

然而,使用java.text。SimpleDateFormat,我无法转换正确格式化的字符串2010-01-01T12:00:00+01:00。我必须首先将其转换为2010-01-01T12:00:00+0100,不带冒号。

目前的解决方案是

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));

这显然不太好。是我错过了什么,还是有更好的解决方案?


回答

感谢JuanZe的评论,我发现了Joda-Time魔法,这里也有描述。

所以解是

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

或者更简单地说,通过构造函数使用默认解析器:

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

对我来说,这很好。


当前回答

另一种解析ISO8601时间戳的简单方法是使用org.apache.commons.lang.time.DateUtils:

import static org.junit.Assert.assertEquals;

import java.text.ParseException;
import java.util.Date;
import org.apache.commons.lang.time.DateUtils;
import org.junit.Test;

public class ISO8601TimestampFormatTest {
  @Test
  public void parse() throws ParseException {
    Date date = DateUtils.parseDate("2010-01-01T12:00:00+01:00", new String[]{ "yyyy-MM-dd'T'HH:mm:ssZZ" });
    assertEquals("Fri Jan 01 12:00:00 CET 2010", date.toString());
  }
}

其他回答

DatatypeConverter解决方案并不适用于所有虚拟机。以下是我的工作:

javax.xml.datatype.DatatypeFactory.newInstance().newXMLGregorianCalendar("2011-01-01Z").toGregorianCalendar().getTime()

我发现joda不能开箱即用(特别是对于我上面给出的日期上的时区的示例,它应该是有效的)

像这样使用字符串 LocalDate.parse (((String) data.get(“d_iso8601”)),DateTimeFormatter.ISO_DATE)

正如其他人所提到的,Android并没有一个很好的方法来支持使用SDK中包含的类来解析/格式化ISO 8601日期。我已经多次编写了这段代码,所以我最终创建了一个Gist,其中包括一个DateUtils类,支持格式化和解析ISO 8601和RFC 1123日期。Gist还包括一个测试用例,显示它支持什么。

https://gist.github.com/mraccola/702330625fad8eebe7d3

我认为很多人想做的是解析JSON日期字符串。如果您来到这个页面,很可能希望将JavaScript JSON日期转换为Java日期。

要显示JSON日期字符串的样子:

    var d=new Date();
    var s = JSON.stringify(d);

    document.write(s);
    document.write("<br />"+d);


    "2013-12-14T01:55:33.412Z"
    Fri Dec 13 2013 17:55:33 GMT-0800 (PST)

JSON日期字符串是2013-12-14T01:55:33.412Z。

JSON规范没有涵盖日期,但上面是一个非常具体的ISO 8601格式,而ISO_8601要大得多,它只是一个非常重要的子集。

参见http://www.json.org 参见http://en.wikipedia.org/wiki/ISO_8601 参见http://www.w3.org/TR/NOTE-datetime

碰巧我写了一个JSON解析器和一个PLIST解析器,它们都使用ISO-8601,但不是相同的位。

/*
    var d=new Date();
    var s = JSON.stringify(d);

    document.write(s);
    document.write("<br />"+d);


    "2013-12-14T01:55:33.412Z"
    Fri Dec 13 2013 17:55:33 GMT-0800 (PST)


 */
@Test
public void jsonJavaScriptDate() {
    String test =  "2013-12-14T01:55:33.412Z";

    Date date = Dates.fromJsonDate ( test );
    Date date2 = Dates.fromJsonDate_ ( test );

    assertEquals(date2.toString (), "" + date);

    puts (date);
}

我为我的项目写了两种方法。一个标准,一个快速。

同样,JSON日期字符串是ISO 8601....的一个非常具体的实现

(我在另一个答案中发布了另一个答案,它应该适用于PLIST日期,这是不同的ISO 8601格式)。

JSON日期如下:

public static Date fromJsonDate_( String string ) {

    try {

        return new SimpleDateFormat ( "yyyy-MM-dd'T'HH:mm:ss.SSSXXX").parse ( string );
    } catch ( ParseException e ) {
        return Exceptions.handle (Date.class, "Not a valid JSON date", e);
    }


}

PLIST文件(ASCII非GNUNext)也使用ISO 8601,但没有毫秒,所以…并不是所有的ISO-8601日期都是相同的。(至少我还没有找到一个使用milis的解析器,我看到的解析器完全跳过时区OMG)。

现在是快速版(你可以在Boon上找到它)。

public static Date fromJsonDate( String string ) {

    return fromJsonDate ( Reflection.toCharArray ( string ), 0, string.length () );

}

注意反射。toCharArray如果可用则使用unsafe,但默认为string。如果不是toCharArray。

(您可以通过替换Reflection将其从示例中删除。toCharArray(string)使用string.toCharArray())。

public static Date fromJsonDate( char[] charArray, int from, int to ) {

    if (isJsonDate ( charArray, from, to )) {
        int year = CharScanner.parseIntFromTo ( charArray, from + 0, from + 4 );
        int month = CharScanner.parseIntFromTo ( charArray,  from +5,  from +7 );
        int day = CharScanner.parseIntFromTo ( charArray,  from +8,  from +10 );
        int hour = CharScanner.parseIntFromTo ( charArray,  from +11,  from +13 );

        int minute = CharScanner.parseIntFromTo ( charArray,  from +14,  from +16 );

        int second = CharScanner.parseIntFromTo ( charArray,  from +17,  from +19 );

        int miliseconds = CharScanner.parseIntFromTo ( charArray,  from +20,  from +23 );

        TimeZone tz = TimeZone.getTimeZone ( "GMT" );


        return toDate ( tz, year, month, day, hour, minute, second, miliseconds );

    }   else {
        return null;
    }

}

isJsonDate的实现如下:

public static boolean isJsonDate( char[] charArray, int start, int to ) {
    boolean valid = true;
    final int length = to -start;

    if (length != JSON_TIME_LENGTH) {
        return false;
    }

    valid &=  (charArray [ start + 19 ]  == '.');

    if (!valid) {
        return false;
    }


    valid &=  (charArray[  start +4 ]  == '-') &&
            (charArray[  start +7 ]  == '-') &&
            (charArray[  start +10 ] == 'T') &&
            (charArray[  start +13 ] == ':') &&
            (charArray[  start +16 ] == ':');

    return valid;
}

无论如何……我猜来这里的人相当多。可能正在寻找JSON日期字符串,虽然它是一个ISO-8601日期,它是一个非常具体的,需要一个非常具体的解析。

public static int parseIntFromTo ( char[] digitChars, int offset, int to ) {
    int num = digitChars[ offset ] - '0';
    if ( ++offset < to ) {
        num = ( num * 10 ) + ( digitChars[ offset ] - '0' );
        if ( ++offset < to ) {
            num = ( num * 10 ) + ( digitChars[ offset ] - '0' );
            if ( ++offset < to ) {
                num = ( num * 10 ) + ( digitChars[ offset ] - '0' );
                if ( ++offset < to ) {
                    num = ( num * 10 ) + ( digitChars[ offset ] - '0' );
                    if ( ++offset < to ) {
                        num = ( num * 10 ) + ( digitChars[ offset ] - '0' );
                        if ( ++offset < to ) {
                            num = ( num * 10 ) + ( digitChars[ offset ] - '0' );
                            if ( ++offset < to ) {
                                num = ( num * 10 ) + ( digitChars[ offset ] - '0' );
                                if ( ++offset < to ) {
                                    num = ( num * 10 ) + ( digitChars[ offset ] - '0' );
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    return num;
}

参见https://github.com/RichardHightower/boon Boon有一个PLIST解析器(ASCII)和一个JSON解析器。

JSON解析器是我所知道的最快的Java JSON解析器。

由加特林表演部的人独立验证。

https://github.com/gatling/json-parsers-benchmark

Benchmark                               Mode Thr     Count  Sec         Mean   Mean error        Units
BoonCharArrayBenchmark.roundRobin      thrpt  16        10    1   724815,875    54339,825    ops/s
JacksonObjectBenchmark.roundRobin      thrpt  16        10    1   580014,875   145097,700    ops/s
JsonSmartBytesBenchmark.roundRobin     thrpt  16        10    1   575548,435    64202,618    ops/s
JsonSmartStringBenchmark.roundRobin    thrpt  16        10    1   541212,220    45144,815    ops/s
GSONStringBenchmark.roundRobin         thrpt  16        10    1   522947,175    65572,427    ops/s
BoonDirectBytesBenchmark.roundRobin    thrpt  16        10    1   521528,912    41366,197    ops/s
JacksonASTBenchmark.roundRobin         thrpt  16        10    1   512564,205   300704,545    ops/s
GSONReaderBenchmark.roundRobin         thrpt  16        10    1   446322,220    41327,496    ops/s
JsonSmartStreamBenchmark.roundRobin    thrpt  16        10    1   276399,298   130055,340    ops/s
JsonSmartReaderBenchmark.roundRobin    thrpt  16        10    1    86789,825    17690,031    ops/s

它拥有最快的JSON解析器,用于流、读取器、bytes[]、char[]、CharSequence (StringBuilder、CharacterBuffer)和String。

查看更多基准测试:

https://github.com/RichardHightower/json-parsers-benchmark

java.time

java。time API(内置于Java 8及更高版本中),使此操作更容易一些。

如果知道输入是UTC,比如末尾的Z(代表Zulu), Instant类就可以解析。

java.util.Date date = Date.from( Instant.parse( "2014-12-12T10:39:40Z" ));

如果您的输入可能是另一个offset-from-UTC值,而不是末尾的Z (Zulu)所指示的UTC,则使用OffsetDateTime类进行解析。

OffsetDateTime odt = OffsetDateTime.parse( "2010-01-01T12:00:00+01:00" );

然后提取一个Instant,并通过调用。

Instant instant = odt.toInstant();  // Instant is always in UTC.
java.util.Date date = java.util.Date.from( instant );