iso8601 formatter - 將ISO 8601兼容的字符串轉換為java.util.Date





simpledateformat doc (21)


好的,這個問題已經得到解答,但我仍然會放棄我的答案。 它可能有助於某人。

我一直在尋找Android解決方案 (API 7)。

  • 喬達無法解決問題 - 這是巨大的,並且受到初始化緩慢的影響。 對於這個特定的目的來說,這似乎也是一個重大的過火。
  • 涉及javax.xml答案在Android API 7上不起作用。

完成這個簡單的課程。 它涵蓋ISO 8601字符串的最常見形式 ,但在某些情況下(當您確信輸入將採用格式時)應該足夠了。

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,以避免Android 2.1中的錯誤 。 如果你和我一樣驚訝,看看這個謎語 。 對於其他Java引擎,您可以將實例緩存在私有靜態字段中(使用ThreadLocal,以確保線程安全)。

我試圖將ISO 8601格式的字符串轉換為java.util.Date。

如果使用Locale(比較樣本),我發現模式“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" ) ;

對我來說,這很好。




Java有十幾種不同的解析日期時間的方法,正如這裡出色的答案所示。 但令人驚訝的是,Java的時間類沒有一個完全實現ISO 8601!

在Java 8中,我建議:

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”。

如果你必須處理這些,我建議首先使用正則表達式來嗅探語法。

這裡有一個很好的ISO 8601示例列表,其中有很多角落案例: https://www.myintervals.com/blog/2009/05/20/iso-8601-date-validation-that-doesnt-suck/ : https://www.myintervals.com/blog/2009/05/20/iso-8601-date-validation-that-doesnt-suck/我是沒有意識到任何可以應對它們的Java類。




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




解析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());
  }
}



SimpleDateFormat for JAVA 1.7為ISO 8601格式提供了一個很酷的模式。

類SimpleDateFormat

這是我所做的:

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



基本功能禮貌:@wrygiel。

該函數可以將ISO8601格式轉換為可處理偏移值的Java Date。 根據ISO 8601定義,偏移量可以用不同的格式提及。

±[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字符串

/*       "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;
}

}




我有類似的需求:我需要能夠解析任何日期ISO8601兼容,而不必事先知道確切的格式,我想要一個輕量級的解決方案,也可以在Android上工作。

當我搜索我的需求時,我偶然發現了這個問題,並註意到AFAIU,沒有回答完全符合我的需求。 於是我開發了jISO8601並將它推到maven central。

只需添加你的pom.xml

<dependency>
  <groupId>fr.turri</groupId>
  <artifactId>jISO8601</artifactId>
  <version>0.2</version>
</dependency>

然後你很好走:

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

希望它有幫助。







java.time

java.time API (內置於Java 8及更高版本)使得這一點更容易一些。

如果你知道輸入是以UTC ,比如結尾的Z (對於祖魯語),那麼Instant類可以解析。

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

如果您的輸入可能是另一個offset-from-UTC值而非UTC結尾的UTC (祖魯語)指示的UTC ,請使用OffsetDateTime類進行分析。

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

然後提取一個Instant ,並通過調用from來轉換為java.util.Date

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



TL;博士

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

喬達時間

更新: Joda-Time項目現在處於維護模式 ,團隊建議遷移到java.time類。 跳轉到下面的Using java.time部分。

使用Joda-Time 2.3嵌入到問題中的答案是正確的,但不必要漫長而復雜。

DateTime類構造函數接受各種ISO 8601格式的String參數。 所以不需要調用解析方法。 你可以在一行代碼中完成

java.util.Date date = new DateTime( "2010-01-01T12:00:00+01:00Z" ).toDate();

時區

問題的嵌入式答案忽略了時區的關鍵問題。 如果不另外指定,則JVM的當前默認時區將隱式分配給新的DateTime對象。 請記住,JVM的當前默認時區可以隨時由JVM中任何線程中的任何代碼進行更改 。 在其他機器上運行時,默認時區也可能不同。

最好指定你想要的/預期的時區。 如果您確實需要默認值,請顯式調用getDefault以自行記錄您的意圖。

示例代碼

這個問題沒有解決時區問題。 因此,這裡是不同時區可能性的示例代碼。

// © 2013 Basil Bourque. This source code may be used freely forever by anyone taking full responsibility for doing so.
// import org.joda.time.*;

// Default time zone.
DateTime dateTimeInMyDefaultTimeZone = new DateTime( "2010-01-01T12:00:00+01:00" );

// UTC
DateTime dateTimeInUtc = new DateTime( "2010-01-01T12:00:00+01:00", DateTimeZone.UTC );

// Named time zone
// Time Zone list… http://joda-time.sourceforge.net/timezones.html
DateTimeZone kolkataTimeZone = DateTimeZone.forID( "Asia/Kolkata" );
DateTime dateTimeInKolkata = new DateTime( "2010-01-01T12:00:00+01:00", kolkataTimeZone );

// Hard-coded to that one-hour offset. Using a named time zone would be better, to handle Daylight Saving Time (DST) or other anomalies.
DateTimeZone timeZoneOffsetOneHour = DateTimeZone.forOffsetHours( 1 );
DateTime dateTimeInOneHourOffset = new DateTime( "2010-01-01T12:00:00+01:00", timeZoneOffsetOneHour );

// Using a named time zone to handle Daylight Saving Time (DST) or other anomalies.
// Arbitrarily picking Algiers as a one-hour offset.
DateTimeZone timeZoneAlgiers = DateTimeZone.forID( "Africa/Algiers" );
DateTime dateTimeAlgiers = new DateTime( "2010-01-01T12:00:00+01:00", timeZoneAlgiers );

轉儲到控制台...

 System.out.println( "dateTimeInMyDefaultTimeZone: " + dateTimeInMyDefaultTimeZone );
System.out.println( "dateTimeInUtc: " + dateTimeInUtc );
System.out.println( "dateTimeInKolkata: " + dateTimeInKolkata );
System.out.println( "dateTimeInOneHourOffset: " + dateTimeInOneHourOffset );
System.out.println( "dateTimeAlgiers: " + dateTimeAlgiers );

運行時...

dateTimeInMyDefaultTimeZone: 2010-01-01T03:00:00.000-08:00
dateTimeInUtc: 2010-01-01T11:00:00.000Z
dateTimeInKolkata: 2010-01-01T16:30:00.000+05:30
dateTimeInOneHourOffset: 2010-01-01T12:00:00.000+01:00
dateTimeAlgiers: 2010-01-01T12:00:00.000+01:00

使用java.time

Java 8及更高版本中的新java.time包受Joda-Time的啟發。

OffsetDateTime類表示時間軸上的一個時刻, offset-from-UTC但不是時區。

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

調用toString以標準ISO 8601格式生成一個字符串:

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

要通過UTC鏡頭查看相同的值,請提取Instant或將偏移量從+01:00調整為00:00

Instant instant = odt.toInstant();  

…要么…

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

如果需要,調整為時區。 時區是一個區域offset-from-UTC值的歷史記錄,具有處理異常情況(如夏令時(DST))的一組規則。 因此,盡可能應用時區而不是單純的偏移量。

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

關於java.time

java.time框架內置於Java 8及更高版本中。 這些類取代了諸如java.util.DateCalendarSimpleDateFormat等麻煩的舊式legacy日期時間類。

現在處於維護模式Joda-Time項目建議遷移到java.time類。

要了解更多信息,請參閱Oracle教程 。 並蒐索堆棧溢出了很多例子和解釋。 規範是JSR 310

何處獲取java.time類?

  • Java SE 8SE 9及更高版本
    • 內置。
    • 帶有捆綁實現的標準Java API的一部分。
    • Java 9增加了一些次要功能和修復。
  • Java SE 6SE 7
  • Android
    • ThreeTenABP項目專門針對Android採用了ThreeTen-Backport (上文提到)。
    • 請參閱如何使用ThreeTenABP ...。

ThreeTen-Extra項目將java.time擴展到其他類。 這個項目是未來可能增加java.time的一個試驗場。 您可以在這裡找到一些有用的課程,例如IntervalYearWeekYearQuarter等。




I think what a lot of people want to do is parse JSON date strings. There is a good chance if you come to this page that you might want to convert a JavaScript JSON date to a Java date.

To show what a JSON date string looks like:

    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)

The JSON date string is 2013-12-14T01:55:33.412Z.

Dates are not covered by JSON spec per say, but the above is a very specific ISO 8601 format, while ISO_8601 is much much bigger and that is a mere subset albeit a very important one.

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

As it happens I wrote a JSON parser and a PLIST parser both of which use ISO-8601 but not the same bits.

/*
    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);
}

I wrote two ways to do this for my project. One standard, one fast.

Again, JSON date string is a very specific implementation of ISO 8601....

(I posted the other one in the other answer which should work for PLIST dates, which are a different ISO 8601 format).

The JSON date is as follows:

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 files (ASCII non GNUNext) also uses ISO 8601 but no miliseconds so... not all ISO-8601 dates are the same. (At least I have not found one that uses milis yet and the parser I have seen skip the timezone altogether OMG).

Now for the fast version (you can find it in Boon).

public static Date fromJsonDate( String string ) {

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

}

Note that Reflection.toCharArray uses unsafe if available but defaults to string.toCharArray if not.

(You can take it out of the example by replacing Reflection.toCharArray ( string ) with 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;
    }

}

The isJsonDate is implemented as follows:

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

Anyway... my guess is that quite a few people who come here.. might be looking for the JSON Date String and although it is an ISO-8601 date, it is a very specific one that needs a very specific parse.

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

See https://github.com/RichardHightower/boon Boon has a PLIST parser (ASCII) and a JSON parser.

The JSON parser is the fastest Java JSON parser that I know of.

Independently verified by the Gatling Performance dudes.

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

It has the fastest JSON parser for streams, readers, bytes[], char[], CharSequence (StringBuilder, CharacterBuffer), and String.

See more benchmarks at:

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




Java 7+的解決方法是使用SimpleDateFormat:
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'
我們需要使用SimpleDateFormat Java 6中可讀的格式。

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中發生錯誤時(您可以檢測Java版本並用if語句替換try / catch),將[ Z+0000 ]或[ +01:00+0100 ]替換。







我面臨同樣的問題,並通過下面的代碼解決它。

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

它為我工作得很好。




這似乎對我最有效:

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字符串中最常見的形式,也是一個很好的子集。

The examples they give are:

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.

I also have a fast version:

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

}

...

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

I have not benchmarked it, but I am guess it will be pretty fast. It seems to work. :)

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



對於Java版本7

您可以遵循Oracle文檔: SimpleDateFormat : SimpleDateFormat

X - 用於ISO 8601時區

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



DatatypeConverter解決方案不適用於所有虛擬機。 以下適用於我:

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

我發現,喬達並沒有開箱即用(特別是我在上面給出的日期時區的例子,這應該是有效的)




我認為我們應該使用

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

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










1 ...

這取決於實現,但對於Java來說,它將是相同的 而不是壞的,因為Java具有靜態唯一種子原子長度,每次創建Random時都會對其進行操作 。 但是我不會對其他語言或實現情況感到驚訝,事實並非如此,他們可能只使用系統時間(Java也使用系統時間,但組合使用唯一種子)。 在某些系統上,您可以獲得多個線程的相同種子。

經過進一步的檢查和一些實際的測試(雖然是脆弱的測試),看起來我可能錯了之前 ,實際上更糟糕的是使用很多(我說的是100k)同時的隨機數生成器(即使它們是不同的實例) )。 我不完全確定它的種子衝突還是實際全球種子增量變得可預測的事實。 當然,這可能只是我的測試工具或方法。

根據維基百科:

隨機數生成器,特別是並行計算機,不應該被信任。[12] 強烈建議使用多個RNG檢查模擬結果,以檢查是否引入了偏差。 在並行計算機上使用的推薦發生器包括使用序列分裂的組合線性同餘生成器和使用獨立序列的滯後Fibonacci生成器。

因此理論上它應該更好,因為ThreadLocalRandom會創建獨立的序列,所以我的測試可能存在缺陷。

這當然基於偽隨機。

物理隨機性或基於實際熵的安全隨機生成器可能導致差異(即更多/更少的熵),但我不是專家,我無法訪問一個。

2 ...

我不能提出一個特定的用例,但可能是你使用了一個不斷創建和處理線程的ExecutorService(假設它們沒有對此的控制)但是從來沒有多次(即最多2個並發線程) 。 您可能會發現ThreadLocalRandom更昂貴,而不是創建單個共享Random。

給出評論的另一個原因和可能更好的理由是您可能想要為所有進程重置種子。 如果你有一個使用線程的遊戲(不是很多但是讓我們假裝)你可能想要全局重置種子用於測試目的,使用AtomicReference到Random要比嘗試將消息傳遞給所有正在運行的線程容易得多。

您可能不想使用ThreadLocalRandom的另一個原因是平台原因。 某些平台對線程創建有特定要求,因此對線程局部創建有特定要求。 因此,要解決“ 你有一個更大的問題,而不是randoms ”,請查看Google Apps

Java應用程序可以創建新線程,但是對於如何執行它有一些限制。 這些線程不能“創建”創建它們的請求“壽命”。 (在後端服務器上,應用程序可以生成後台線程,這個線程可以“創建”創建它的請求“壽命”。)

並解決您為什麼要使用無法重用線程的ExecutorService的額外註釋:

或者使用com.google.appengine.api.ThreadManager.currentRequestThreadFactory()返回的工廠對象和ExecutorService(例如,調用Executors.newCachedThreadPool(factory))。

即一個不一定重用線程的ThreadPool。







java date iso8601