将符合ISO 8601的字符串转换为java.util.Date

Converting ISO 8601-compliant String to java.util.Date

我正试图将一个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,没有结肠。

所以,目前的解决方案是

1
2
3
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时代的魔力,这里也描述了它。

所以,解决办法是

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

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

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

对我来说,这很好。


不幸的是,SimuleDead格式(Java 6和更早版本)可用的时区格式不是ISO 8601兼容的。SimpleDateFormat理解时区字符串,如"GMT+01:00"或"+0100",后者根据RFC_822。

即使Java 7根据ISO 8601增加了对时区描述符的支持,SimuleDead格式仍然无法正确解析完整的日期字符串,因为它不支持可选的部分。

当然,可以使用regexp重新格式化输入字符串,但替换规则并不像问题中那样简单:

  • 某些时区不是离开UTC的整小时,因此字符串不一定以":00"结尾。
  • ISO8601只允许时区包含小时数,因此"+01"等于"+01:00"
  • ISO8601允许使用"Z"来表示UTC,而不是"+00:00"。

更简单的解决方案可能是在JAXB中使用数据类型转换器,因为JAXB必须能够根据XML模式规范解析ISO8601日期字符串。javax.xml.bind.DatatypeConverter.parseDateTime("2010-01-01T12:00:00Z")会给你一个Calendar对象,如果你需要Date对象,你可以简单地在上面使用gettime()。

也许你也可以利用乔达时间,但我不知道你为什么要为此而烦恼。


好的,这个问题已经回答了,但我还是放弃我的答案。它可能对某人有帮助。

我一直在寻找Android(API 7)的解决方案。

  • 乔达是不可能的-它是巨大的,并遭受缓慢的初始化。对于那个特定的目的来说,这似乎也是一个重大的过度杀伤力。
  • 涉及javax.xml的答案在android api 7上不起作用。

最后实现了这个简单的类。它只涵盖最常见的ISO 8601字符串形式,但在某些情况下(当您确信输入将采用这种格式时)这就足够了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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;
    }
}

性能说明:我每次都会实例化新的simpledateformat,以避免Android2.1中出现错误。如果你和我一样惊讶,看看这个谜语。对于其他Java引擎,可以将实例缓存在专用静态字段中(使用THeLead本地,以线程安全)。


被Java 7文档所支持的方式:

1
2
3
4
5
6
7
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);

您可以在simpledateformat javadoc的部分示例中找到更多示例。


Java.时间

JavaTimeAPI(内置到Java 8和更高版本中)使得这更容易一些。

如果您知道输入是在UTC中的,例如末尾的Z(对于zulu),那么Instant类可以解析。

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

如果您的输入可能是另一个与UTC值的偏移量,而不是由末尾的EDOCX1(zulu)指示的UTC,请使用OffsetDateTime类进行分析。

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

然后提取一个Instant,通过调用from转换为java.util.Date

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


Jackson数据绑定库还具有这样做的iso8601dateformat类(iso8601utils中的实际实现)。

1
2
ISO8601DateFormat df = new ISO8601DateFormat();
Date d = df.parse("2010-07-28T22:25:51Z");


DR

1
OffsetDateTime.parse ("2010-01-01T12:00:00+01:00" )

使用JavaTimes

Java 8和以后的新JavaTime包是由JoDA时间启发的。

OffsetDateTime类表示时间线上的某一时刻,与UTC有偏移,但不是时区。

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

调用toString生成标准ISO 8601格式的字符串:

2010-01-01T12:00+01:00

要通过UTC透镜看到相同的值,请提取一个Instant或调整从+01:0000:00的偏移量。

1
Instant instant = odt.toInstant();

…或…

1
OffsetDateTime odtUtc = odt.withOffsetSameInstant( ZoneOffset.UTC );

如果需要,调整到时区。时区是一个区域相对于UTC值的偏移历史,具有一组处理异常的规则,例如夏令时(DST)。因此,只要有可能,就应用时区,而不仅仅是偏移。

1
ZonedDateTime zonedDateTimeMontréal = odt.atZoneSameInstant( ZoneId.of("America/Montreal" ) );

关于JavaTimes

JavaTimeFr框架是在Java 8和之后构建的。这些类取代了麻烦的旧遗留日期时间类,如java.util.DateCalendarSimpleDateFormat

现在处于维护模式的joda time项目建议迁移到java.time类。

要了解更多信息,请参阅Oracle教程。以及搜索堆栈溢出以获得许多示例和解释。规格为JSR 310。

您可以直接与数据库交换java.time对象。使用符合JDBC 4.2或更高版本的JDBC驱动程序。不需要字符串,不需要java.sql.*类。

在哪里获取java.time类?

  • Java SE 8、Java SE 9、Java SE 10以及以后
    • 内置的。
    • 标准JAVA API的一部分与捆绑实现。
    • Java 9添加了一些小的特性和修复。
  • Java SE 6和Java SE 7
    • 大部分JavaTimeActudio都被移植到TealEnter后端的Java 6和7中。
  • 安卓
    • java.time类的Android包实现的更高版本。
    • 对于早期的Android(<26),threetenabp项目适应threeten backport(如上所述)。看看如何使用三连珠……

threeten额外项目使用额外的类扩展java.time。这个项目是将来可能添加到java.time的一个试验场。您可以在这里找到一些有用的类,如IntervalYearWeekYearQuarter等等。


对于Java版本7

您可以遵循Oracle文档:http://docs.oracle.com/javase/7/docs/api/java/text/simpledateformat.html

X-用于ISO 8601时区

1
2
3
4
5
6
7
8
9
10
11
12
TimeZone tz = TimeZone.getTimeZone("UTC");
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
df.setTimeZone(tz);
String nowAsISO = df.format(new Date());

System.out.println(nowAsISO);

DateFormat df1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
//nowAsISO ="2013-05-31T00:00:00Z";
Date finalResult = df1.parse(nowAsISO);

System.out.println(finalResult);


数据类型转换器解决方案不适用于所有虚拟机。以下对我有效:

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

我发现Joda不是开箱即用的(特别是我在上面给出的例子中,时区是在一个日期,它应该是有效的)


我想我们应该用

1
DateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")

日期:2010-01-01T12:00:00Z


解析iso8601时间戳的另一个非常简单的方法是使用org.apache.commons.lang.time.DateUtils

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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());
  }
}

Java.时间

请注意,在Java 8中,可以使用JavaTime.ZONDATDeTimeType及其静态EDCOX1引用0方法。


Java 7 +的解决方案是使用SimuleDead格式:DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX", Locale.US);

此代码可以解析如下ISO8601格式:

  • 2017-05-17T06:01:43.785Z
  • 2017-05-13T02:58:21.391+01:00

但是在java6上,SimpleDateFormat不理解X的字符,会抛出IllegalArgumentException: Unknown pattern character 'X'我们需要将ISO8601日期归一化到Java 6中使用EDCOX1×9表示的格式。

1
2
3
4
5
6
7
8
9
10
11
12
public static Date iso8601Format(String formattedDate) throws ParseException {
    try {
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX", Locale.US);
        return df.parse(formattedDate);
    } catch (IllegalArgumentException ex) {
        // error happen in Java 6: Unknown pattern character 'X'
        if (formattedDate.endsWith("Z")) formattedDate = formattedDate.replace("Z","+0000");
        else formattedDate = formattedDate.replaceAll("([+-]\\d\\d):(\\d\\d)\\s*$","$1$2");
        DateFormat df1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.US);
        return df1.parse(formattedDate);
    }
}

上面的方法在Java 6中发生错误时,用EDCOX1〔14〕〕或[EDCOX1〔15〕用EDCOX1〔16〕〕代替(当您可以检测Java版本并用IF语句替换TIV/catch)时。


也可以使用以下类-

1
2
3
4
org.springframework.extensions.surf.util.ISO8601DateFormat


Date date = ISO8601DateFormat.parse("date in iso8601");

链接到JavaDOC层次结构的包Or.SrrgFrasWork.ExtExx.Surf.Mav.PuxiN.UTL


我也遇到了同样的问题,用下面的代码解决了它。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 public static Calendar getCalendarFromISO(String datestring) {
    Calendar calendar = Calendar.getInstance(TimeZone.getDefault(), Locale.getDefault()) ;
    SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault());
    try {
        Date date = dateformat.parse(datestring);
        date.setHours(date.getHours() - 1);
        calendar.setTime(date);

        String test = dateformat.format(calendar.getTime());
        Log.e("TEST_TIME", test);

    } catch (ParseException e) {
        e.printStackTrace();
    }

    return calendar;
}

我以前用过SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.getDefault());

但后来我发现造成这一例外的主要原因是yyyy-MM-dd'T'HH:mm:ss.SSSZ

所以我用过

SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault());

对我来说很管用。


ApacheJackrabbit使用iso8601格式来持久化日期,并且有一个助手类来解析它们:

org.apache.jackrabbit.util.iso8601(英文)

与jackrabbit jcr commons一起提供。


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

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


Java 1.7的SimuleDead格式对于ISO 8601格式有一个很酷的模式。

类simpledateformat

以下是我所做的:

1
2
Date d = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ",
         Locale.ENGLISH).format(System.currentTimeMillis());


Java有十几种不同的方法来解析日期时间,正如这里的优秀答案所证明的那样。但有点令人惊讶的是,Java的时间类没有一个完全实现ISO 8601!

用Java 8,我建议:

1
2
ZonedDateTime zp = ZonedDateTime.parse(string);
Date date = Date.from(zp.toInstant());

这将处理UTC和带偏移量的示例,如"2017-09-13t10:36:40z"或"2017-09-13t10:36:40+01:00"。它适用于大多数用例。

但它不会处理"2017-09-13T10:36:40+01"这样的例子,这是一个有效的ISO 8601日期时间。它也不会只处理日期,例如"2017-09-13"。

如果必须处理这些问题,我建议首先使用regex来嗅探语法。

这里有一个很好的ISO 8601示例列表,其中有很多角落的例子:HTTPS://www. MyTalvals/COM/Bug/09/05/20/ISO-8601-DAT-ValueTest-To-Dunn-Sukk/我不知道任何Java类可以应付所有这些Java类。


使用字符串类LocalDate.parse(((String) data.get("d_iso8601")),DateTimeFormatter.ISO_DATE)


这样做:

1
2
3
4
5
6
7
8
public static void main(String[] args) throws ParseException {

    String dateStr ="2016-10-19T14:15:36+08:00";
    Date date = javax.xml.bind.DatatypeConverter.parseDateTime(dateStr).getTime();

    System.out.println(date);

}

以下是输出:

星期三10月19日15:15:36 CST 2016


为了格式化这样的日期,下面的工作在Java 6应用程序中为我工作。胸腺素项目中有一个DateFormatJacksonThymeleafISO8601DateFormat,插入缺失的结肠:

https://github.com/thymeleaf/thymeleaf/blob/40d27f44df7b52eda47d1bc6f1b301add6098b3/src/main/java/org/thymeleaf/standard/serializer/standardjavascriptserializer.java

我用它来实现ecmascript日期格式的兼容性。


我有一个类似的需求:我需要能够在不预先知道确切格式的情况下解析任何符合ISO8601的日期,并且我想要一个轻量级的解决方案,它也可以在Android上工作。

当我在谷歌上搜索我的需求时,我偶然发现了这个问题,并注意到阿法乌没有完全符合我需求的答案。所以我开发了jiso8601并把它推到Maven Central上。

再加上你的pom.xml

1
2
3
4
5
<dependency>
  <groupId>fr.turri</groupId>
  jISO8601</artifactId>
  <version>0.2</version>
</dependency>

然后你就可以走了:

1
2
3
4
import fr.turri.jiso8601.*;
...
Calendar cal = Iso8601Deserializer.toCalendar("1985-03-04");
Date date = Iso8601Deserializer.toDate("1985-03-04T12:34:56Z");

希望能有所帮助。


这似乎对我最好:

1
2
3
4
5
6
7
8
9
10
public static Date fromISO8601_( String string ) {

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


}

我需要将JavaScript日期字符串转换为Java。我发现上面的建议是可行的。有一些使用simpledateformat的示例非常接近,但它们似乎不是以下建议的子集:

http://www.w3.org/tr/note-datetime

我需要plist和javascript字符串等支持。

这似乎是最常见的ISO8601字符串形式,是一个很好的子集。

他们给出的例子有:

1
2
3
4
1994-11-05T08:15:30-05:00 corresponds
November 5, 1994, 8:15:30 am, US Eastern Standard Time.

 1994-11-05T13:15:30Z corresponds to the same instant.

我还有一个快速版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
final static int SHORT_ISO_8601_TIME_LENGTH = "1994-11-05T08:15:30Z".length ();
                                            // 01234567890123456789012
final static int LONG_ISO_8601_TIME_LENGTH ="1994-11-05T08:15:30-05:00".length ();


public static Date fromISO8601( String string ) {
    if (isISO8601 ( string )) {
        char [] charArray = Reflection.toCharArray ( string );//uses unsafe or string.toCharArray if unsafe is not available
        int year = CharScanner.parseIntFromTo ( charArray, 0, 4 );
        int month = CharScanner.parseIntFromTo ( charArray, 5, 7 );
        int day = CharScanner.parseIntFromTo ( charArray, 8, 10 );
        int hour = CharScanner.parseIntFromTo ( charArray, 11, 13 );

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

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

        TimeZone tz ;

         if (charArray[19] == 'Z') {

             tz = TimeZone.getTimeZone ("GMT" );
         } else {

             StringBuilder builder = new StringBuilder ( 9 );
             builder.append ("GMT" );
             builder.append( charArray, 19, LONG_ISO_8601_TIME_LENGTH - 19);
             String tzStr = builder.toString ();
             tz = TimeZone.getTimeZone ( tzStr ) ;

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

    }   else {
        return null;
    }

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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;
}


public static boolean isISO8601( String string ) {
      boolean valid = true;

      if (string.length () == SHORT_ISO_8601_TIME_LENGTH) {
          valid &=  (string.charAt ( 19 )  == 'Z');

      } else if (string.length () == LONG_ISO_8601_TIME_LENGTH) {
          valid &=  (string.charAt ( 19 )  == '-' || string.charAt ( 19 )  == '+');
          valid &=  (string.charAt ( 22 )  == ':');

      } else {
          return false;
      }

    //  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4
    //"1 9 9 4 - 1 1 - 0 5 T 0 8 : 1 5 : 3 0 - 0 5 : 0 0

    valid &=  (string.charAt ( 4 )  == '-') &&
                (string.charAt ( 7 )  == '-') &&
                (string.charAt ( 10 ) == 'T') &&
                (string.charAt ( 13 ) == ':') &&
                (string.charAt ( 16 ) == ':');

    return valid;
}

我没有把它作为基准,但我想它会很快。它似乎起作用了。:)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Test
public void testIsoShortDate() {
    String test = "1994-11-05T08:15:30Z";

    Date date = Dates.fromISO8601 ( test );
    Date date2 = Dates.fromISO8601_ ( test );

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

    puts (date);
}

@Test
public void testIsoLongDate() {
    String test = "1994-11-05T08:11:22-05:00";

    Date date = Dates.fromISO8601 ( test );
    Date date2 = Dates.fromISO8601_ ( test );

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

    puts (date);
}

基本功能礼节:@wrygiel.

这个函数可以将ISO8601格式转换为可以处理偏移值的Java日期。根据ISO 8601的定义,可以用不同的格式提及偏移量。

1
2
3
4
5
±[hh]:[mm]
±[hh][mm]
±[hh]

Eg: "18:30Z","22:30+04","1130-0700", and"15:00-03:30" all mean the same time. - 06:30PM UTC

此类具有要转换的静态方法

  • ISO8601字符串结束日期(本地时区)对象
  • 日期到ISO8601字符串
  • 夏令时自动计算

示例ISO8601字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/*      "2013-06-25T14:00:00Z";
        "2013-06-25T140000Z";
        "2013-06-25T14:00:00+04";
        "2013-06-25T14:00:00+0400";
        "2013-06-25T140000+0400";
        "2013-06-25T14:00:00-04";
        "2013-06-25T14:00:00-0400";
        "2013-06-25T140000-0400";*/



public class ISO8601DateFormatter {

private static final DateFormat DATE_FORMAT_1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
private static final DateFormat DATE_FORMAT_2 = new SimpleDateFormat("yyyy-MM-dd'T'HHmmssZ");
private static final String UTC_PLUS ="+";
private static final String UTC_MINUS ="-";

public static Date toDate(String iso8601string) throws ParseException {
    iso8601string = iso8601string.trim();
    if(iso8601string.toUpperCase().indexOf("Z")>0){
        iso8601string = iso8601string.toUpperCase().replace("Z","+0000");
    }else if(((iso8601string.indexOf(UTC_PLUS))>0)){
        iso8601string = replaceColon(iso8601string, iso8601string.indexOf(UTC_PLUS));
        iso8601string = appendZeros(iso8601string, iso8601string.indexOf(UTC_PLUS), UTC_PLUS);
    }else if(((iso8601string.indexOf(UTC_MINUS))>0)){
        iso8601string = replaceColon(iso8601string, iso8601string.indexOf(UTC_MINUS));
        iso8601string = appendZeros(iso8601string, iso8601string.indexOf(UTC_MINUS), UTC_MINUS);
    }

    Date date = null;
    if(iso8601string.contains(":"))
        date = DATE_FORMAT_1.parse(iso8601string);
    else{
        date = DATE_FORMAT_2.parse(iso8601string);
    }
    return date;
}

public static String toISO8601String(Date date){
    return DATE_FORMAT_1.format(date);
}

private static String replaceColon(String sourceStr, int offsetIndex){
    if(sourceStr.substring(offsetIndex).contains(":"))
        return sourceStr.substring(0, offsetIndex) + sourceStr.substring(offsetIndex).replace(":","");
    return sourceStr;
}

private static String appendZeros(String sourceStr, int offsetIndex, String offsetChar){
    if((sourceStr.length()-1)-sourceStr.indexOf(offsetChar,offsetIndex)<=2)
        return sourceStr +"00";
    return sourceStr;
}

}


我认为很多人想要做的是解析JSON日期字符串。如果您希望将JavaScript JSON日期转换为Java日期,则有一个很好的机会。

要显示JSON日期字符串的外观,请执行以下操作:

1
2
3
4
5
6
7
8
9
    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_要大得多,这只是一个子集,尽管它非常重要。

见http://www.json.org参见http://en.wikipedia.org/wiki/iso_请参阅http://www.w3.org/tr/note-datetime

碰巧,我编写了一个JSON解析器和一个plist解析器,两者都使用ISO-8601,但位不同。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/*
    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的一个非常具体的实现。

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

JSON日期如下:

1
2
3
4
5
6
7
8
9
10
11
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中找到它)。

1
2
3
4
5
public static Date fromJsonDate( String string ) {

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

}

请注意,reflection.tochararray使用不安全(如果可用),但如果不安全,则默认为string.tochararray。

(您可以通过将reflection.tochararray(string)替换为string.tochararray()将其从示例中去掉)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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日期,但它是一个非常具体的日期,需要非常具体的分析。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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/richardhighttower/boonBoon有一个plist解析器(ascii)和一个json解析器。

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

由加特林表演队独立验证。

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

1
2
3
4
5
6
7
8
9
10
11
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

对于流、读卡器、bytes[]、char[]、charsequence(stringbuilder、characterbuffer)和string,它有最快的JSON解析器。

更多基准见:

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