Conversione della stringa conforme a ISO 8601 in java.util.Data


Sto cercando di convertire una stringa formattata ISO 8601 in una java.util.Date.

Ho trovato il modello yyyy-MM-dd'T'HH:mm:ssZ conforme a ISO8601 se utilizzato con una locale (confronta il campione).

Tuttavia, usando java.text.SimpleDateFormat, non riesco a convertire la stringa correttamente formattata 2010-01-01T12:00:00+01:00. Devo convertirlo prima a 2010-01-01T12:00:00+0100, senza i due punti.

Quindi, la soluzione corrente è

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

Che ovviamente non è così bello. Mi manca qualcosa o c'è di meglio soluzione?


Risposta

Grazie al commento di JuanZe, ho trovato la magia Joda-Time, è anche descritta qui.

Quindi, la soluzione è

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

O più semplicemente, usa il parser predefinito tramite il costruttore:

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

Per me, questo è bello.

Author: wittich, 2010-02-04

25 answers

Sfortunatamente, i formati di fuso orario disponibili per SimpleDateFormat (Java 6 e versioni precedenti) non sono conformi a ISO 8601 . SimpleDateFormat comprende stringhe di fuso orario come "GMT + 01: 00" o "+ 0100", quest'ultimo secondo RFC # 822.

Anche se Java 7 ha aggiunto il supporto per i descrittori di fuso orario secondo ISO 8601, SimpleDateFormat non è ancora in grado di analizzare correttamente una stringa di data completa, in quanto non ha il supporto per le parti opzionali.

Riformattazione la tua stringa di input usando regexp è certamente una possibilità, ma le regole di sostituzione non sono così semplici come nella tua domanda:

  • Alcuni fusi orari non hanno le ore intere spente UTC , quindi la stringa non termina necessariamente con ":00".
  • ISO8601 consente di includere solo il numero di ore nel fuso orario, quindi "+ 01 "equivale a" +01: 00 "
  • ISO8601 consente l'utilizzo di "Z" per indicare UTC invece di "+00:00".

La soluzione più semplice è possibilmente per utilizzare il convertitore di tipo di dati in JAXB, poiché JAXB deve essere in grado di analizzare la stringa di data ISO8601 in base alle specifiche dello schema XML. javax.xml.bind.DatatypeConverter.parseDateTime("2010-01-01T12:00:00Z") ti darà un oggetto Calendar e puoi semplicemente usare getTime() su di esso, se hai bisogno di un oggetto Date.

Probabilmente potresti usare anche Joda-Time , ma non capisco perché dovresti preoccuparti di questo.

 429
Author: jarnbjo, 2014-03-08 17:50:49

Ok, questa domanda ha già una risposta, ma lascerò comunque la mia risposta. Potrebbe aiutare qualcuno.

Ho cercato una soluzione per Android (API 7).

  • Joda era fuori questione - è enorme e soffre di inizializzazione lenta. Sembrava anche un grande eccessivo per quel particolare scopo.
  • Le risposte che coinvolgono javax.xml non funzioneranno su Android API 7.

Ha finito per implementare questa semplice classe. Copre solo i più comuni form di stringhe ISO 8601, ma questo dovrebbe essere sufficiente in alcuni casi (quando sei abbastanza sicuro che l'input sarà in questo formato).

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

Nota sulle prestazioni: Istanziare nuovo SimpleDateFormat ogni volta come mezzo per evitare un bug in Android 2.1. Se sei stupito come me, vedi questo enigma . Per altri motori Java, è possibile memorizzare nella cache l'istanza in un campo statico privato (utilizzando ThreadLocal, per essere thread safe).

 191
Author: wrygiel, 2017-05-23 12:10:47

Il modo in cui è benedetto dalla documentazione di Java 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);

Puoi trovare altri esempi nella sezione Esempia SimpleDateFormat javadoc.

 176
Author: Antonio, 2015-11-17 20:24:47

Java.tempo

Il java.time API (integrato in Java 8 e versioni successive), rende questo un po ' più semplice.

Se si sa che l'input è in UTC , ad esempio Z (per Zulu) alla fine, il Instant classe può analizzare.

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

Se il tuo input può essere un altro offset-from-UTC valori anziché UTC indicato dal Z (Zulu) alla fine, usa il OffsetDateTime classe da analizzare.

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

Quindi estrarre un Instant, e convertire in un java.util.Date chiamando from.

Instant instant = odt.toInstant();  // Instant is always in UTC.
java.util.Date date = java.util.Date.from( instant );
 69
Author: Adam, 2017-04-18 09:57:46

La libreria Jackson-databindha anche la classe ISO8601DateFormatche lo fa (implementazione effettiva in ISO8601Utils.

ISO8601DateFormat df = new ISO8601DateFormat();
Date d = df.parse("2010-07-28T22:25:51Z");
 61
Author: david_p, 2013-07-05 14:23:43

Tl;dr

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

Utilizzo di java.ora

Il nuovo java.il pacchetto time in Java 8 e versioni successive è stato ispirato da Joda-Time.

La classe OffsetDateTimerappresenta un momento sulla timeline con un offset da UTC ma non un fuso orario.

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

Chiamare toString genera una stringa nel formato ISO 8601 standard:

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

Per vedere lo stesso valore attraverso la lente di UTC, estrarre un Instant o regolare offset da +01:00 a 00:00.

Instant instant = odt.toInstant();  

Or o {

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

Regolare in un fuso orario se lo si desidera. Un fuso orario è una cronologia di valori di offset da UTC per una regione, con un insieme di regole per la gestione di anomalie come l'ora legale (DST). Quindi applica un fuso orario piuttosto che un semplice offset quando possibile.

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

Informazioni su java.tempo

Il java.tempo framework è integrato in Java 8 e versioni successive. Essi le classi sostituiscono le classi data-ora vecchie legacy come java.util.Date, Calendar, & SimpleDateFormat.

Il Joda-Tempo progetto, ora in modalità di manutenzione , consiglia la migrazione al java.tempo classi.

Per saperne di più, vedere il Tutorial Oracle. E cerca Stack Overflow per molti esempi e spiegazioni. La specifica è JSR 310 .

Puoi scambio java.tempo oggetti direttamente con il database. Utilizzare un driver JDBC compatibile con JDBC 4.2 o successivo. Non c'è bisogno di stringhe, non c'è bisogno di java.sql.* classi.

Dove ottenere il java.lezioni di tempo?

  • Java SE 8, Java SE 9, Java SE 10, e dopo
    • Integrato.
    • Parte dell'API Java standard con un bundle attuazione.
    • Java 9 aggiunge alcune funzionalità e correzioni minori.
  • Java SE 6 e Java SE 7
    • Gran parte del java.la funzionalità time è back-porting su Java 6 e 7 in Treten-Backport.
  • Androide
    • Versioni successive di Android bundle implementazioni di java.lezioni di tempo.
    • Per Android precedente (TRETENABP progetto adatta ThreeTen-Backport (menzionato sopra). Vedi Come usare ThreeTenABP....

Il Tre-Extra progetto estende java.tempo con classi aggiuntive. Questo progetto è un terreno di prova per possibili future aggiunte a java.tempo. Si possono trovare alcune classi utili qui come Interval, YearWeek, YearQuarter, e più .


 33
Author: Basil Bourque, 2018-05-23 02:09:06

Per Java versione 7

È possibile seguire la documentazione Oracle: http://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html

X-viene utilizzato per il fuso orario 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);
 26
Author: d.danailov, 2014-04-08 14:20:28

La soluzione DatatypeConverter non funziona in tutte le macchine virtuali. Il seguente funziona per me:

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

Ho scoperto che joda non funziona fuori dalla scatola (in particolare per l'esempio che ho dato sopra con il fuso orario in una data, che dovrebbe essere valida)

 20
Author: James Scriven, 2013-01-09 11:15:04

Penso che dovremmo usare

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

Per la data 2010-01-01T12:00:00Z

 18
Author: Toby, 2013-12-23 13:56:05

Un altro modo molto semplice per analizzare i timestamp ISO8601 è usare 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());
  }
}
 8
Author: tmandry, 2013-08-22 14:05:35

Java.tempo

Si noti che in Java 8 è possibile utilizzare java.tempo.ZonedDateTime classe e il suo metodo statico parse(CharSequence text).

 6
Author: Martin Rust, 2014-07-20 16:59:08

Ho affrontato lo stesso problema e l'ho risolto con il seguente codice .

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

Prima stavo usando SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.getDefault());

Ma in seguito ho scoperto che la causa principale dell'eccezione era yyyy-MM-dd'T'HH:mm:ss.SSSZ,

Così ho usato

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

Ha funzionato bene per me .

 4
Author: Abhay Kumar, 2015-07-10 09:05:26

Inoltre è possibile utilizzare la seguente classe -

org.springframework.extensions.surf.util.ISO8601DateFormat


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

Collegamento alla gerarchia Java Doc - per l'organizzazione del pacchetto.quadro di primavera.estensione.surf.maven.plugin.util

 4
Author: Sergey Palyukh, 2016-07-28 09:30:16

Apache Jackrabbit utilizza il formato ISO 8601 per le date persistenti e esiste una classe helper per analizzarle:

Org.Apache.jackrabbit.util.ISO8601

Il nostro sito utilizza cookie tecnici e di terze parti.
 3
Author: Alexander Klimetschek, 2013-04-22 10:28:33

Come altri hanno menzionato Android non ha un buon modo per supportare l'analisi/formattazione delle date ISO 8601 utilizzando le classi incluse nell'SDK. Ho scritto questo codice più volte, quindi ho finalmente creato un Gist che include una classe DateUtils che supporta la formattazione e l'analisi delle date ISO 8601 e RFC 1123. Il Gist include anche un test case che mostra ciò che supporta.

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

 3
Author: Matt Accola, 2015-06-20 05:01:12

La soluzione per Java 7 + sta usando SimpleDateFormat:
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX", Locale.US);

Questo codice può analizzare il formato ISO8601 come:

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

Ma su Java6, SimpleDateFormat non capisce il carattere X e lancerà
IllegalArgumentException: Unknown pattern character 'X'
Abbiamo bisogno di normalizzare la data ISO8601 al formato leggibile in Java 6 con SimpleDateFormat.

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

Metodo sopra per sostituire [Z con +0000] o [+01:00 con +0100] quando si verifica un errore in Java 6 (è possibile rilevare Java versione e sostituire try / catch con if istruzione).

 3
Author: Khang .NT, 2017-05-16 09:42:19

SimpleDateFormat per JAVA 1.7 ha un modello fresco per il formato ISO 8601.

Classe SimpleDateFormat

Ecco cosa ho fatto:

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

Java ha una dozzina di modi diversi per analizzare una data-ora, come dimostrano le eccellenti risposte qui. Ma un po ' sorprendentemente, nessuna delle classi temporali di Java implementa pienamente ISO 8601!

Con Java 8, consiglierei:

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

Che gestirà esempi sia in UTC che con un offset, come "2017-09-13T10:36:40Z" o "2017-09-13T10:36:40+01:00". Lo farà per la maggior parte dei casi d'uso.

Ma non gestirà esempi come "2017-09-13T10:36: 40 + 01", che è una ISO 8601 valida data-ora.
Inoltre, non gestirà solo la data, ad esempio "2017-09-13".

Se devi gestirli, suggerirei di usare prima una regex per annusare la sintassi.

C'è una bella lista di esempi ISO 8601 qui con molti casi d'angolo: https://www.myintervals.com/blog/2009/05/20/iso-8601-date-validation-that-doesnt-suck / Non sono a conoscenza di alcuna classe Java che potrebbe far fronte a tutti loro.

 2
Author: Daniel Winterstein, 2017-09-13 11:22:54

Usa una stringa come LocalDate.parse(((String) data.get("d_iso8601")),DateTimeFormatter.ISO_DATE)

 1
Author: Stepan, 2017-07-18 14:57:35

Avevo una necessità simile: avevo bisogno di essere in grado di analizzare qualsiasi data conforme ISO8601 senza conoscere il formato esatto in anticipo, e volevo una soluzione leggera che funzionasse anche su Android.

Quando ho cercato su Google i miei bisogni mi sono imbattuto in questa domanda e ho notato che AFAIU, nessuna risposta si adatta completamente alle mie esigenze. Così ho sviluppato jISO8601 e l'ho spinto su maven central.

Basta aggiungere in voi pom.xml:

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

E poi sei a posto:

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

Lo spera aiutare.

 0
Author: gturri, 2016-01-29 17:30:57

Per formattare una data come questa, il seguente ha funzionato per me in un'applicazione basata su Java 6. C'è una DateFormat classe JacksonThymeleafISO8601DateFormat nel progetto thymeleaf che inserisce i due punti mancanti:

Https://github.com/thymeleaf/thymeleaf/blob/40d27f44df7b52eda47d1bc6f1b3012add6098b3/src/main/java/org/thymeleaf/standard/serializer/StandardJavaScriptSerializer.java

L'ho usato per la compatibilità del formato data ECMAScript.

 0
Author: ekip, 2016-10-09 14:29:20

Farlo in questo modo:

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

}

Ecco l'output:

Mer Ott 19 15:15:36 CST 2016

 0
Author: yinhaomin, 2016-10-19 07:04:30

Funzione di base per gentile concessione: @ wrygiel.

Questa funzione può convertire il formato ISO8601 in data Java che può gestire i valori di offset. Secondo la definizione di ISO 8601 l'offset può essere menzionato in diversi formati.

±[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

Questa classe ha metodi statici per convertire

  • ISO8601 stringa fino alla data (fuso orario locale) oggetto
  • Data alla stringa ISO8601
  • L'ora legale viene automaticamente calc

Campione ISO8601 Stringhe

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

}

 -1
Author: AKh, 2013-06-25 23:28:06

Questo sembrava funzionare meglio per me:

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


}

Avevo bisogno di convertire le stringhe di data JavaScript in Java. Ho trovato le opere di cui sopra con la raccomandazione. C'erano alcuni esempi usando SimpleDateFormat che erano vicini ma non sembravano essere il sottoinsieme come raccomandato da:

Http://www.w3.org/TR/NOTE-datetime

E supportato da stringhe PLIST e JavaScript e tale che è quello di cui avevo bisogno.

Questa sembra essere la forma più comune di ISO8601 stringa là fuori, e un buon sottoinsieme.

Gli esempi che danno sono:

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.

Ho anche una versione veloce:

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

Non l'ho confrontato, ma immagino che sarà piuttosto veloce. Sembra funzionare. :)

@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);
}
 -1
Author: RickHigh, 2013-12-14 00:32:36

Penso che molte persone vogliano analizzare le stringhe di date JSON. C'è una buona probabilità se si arriva a questa pagina che si potrebbe desiderare di convertire una data JSON JavaScript in una data Java.

Per mostrare come appare una stringa di data 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)

La stringa di data JSON è 2013-12-14T01:55: 33.412 Z.

Le date non sono coperte dalle specifiche JSON per dire, ma quanto sopra è un formato ISO 8601 molto specifico, mentre ISO_8601 è molto più grande e questo è un semplice sottoinsieme anche se molto uno importante.

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

Come succede ho scritto un parser JSON e un parser PLIST entrambi usano ISO-8601 ma non gli stessi bit.

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

Ho scritto due modi per farlo per il mio progetto. Uno standard, uno veloce.

Ancora una volta, la stringa di date JSON è un'implementazione molto specifica di ISO 8601....

(ho postato l'altro nel altra risposta che dovrebbe funzionare per le date PLIST, che sono un diverso formato ISO 8601).

La data JSON è la seguente:

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


}

I file PLIST (ASCII non GNUNext) usano anche ISO 8601 ma non milisecondi... non tutte le date ISO-8601 sono uguali. (Almeno non ne ho trovato uno che usi ancora milis e il parser che ho visto saltare del tutto il fuso orario OMG).

Ora per la versione veloce (si può trovare in Boon).

public static Date fromJsonDate( String string ) {

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

}

Si noti che la riflessione.toCharArray utilizza non sicuri se disponibile, ma il valore predefinito è string.toCharArray se non.

(Puoi estrarlo dall'esempio sostituendo Reflection.toCharArray (stringa) con stringa.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;
    }

}

L'isJsonDate è implementato come segue:

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

Comunque... la mia ipotesi è che un bel po ' di persone che vengono qui.. potrebbe essere alla ricerca della stringa di data JSON e sebbene sia una data ISO-8601, è molto specifica che richiede un'analisi molto specifica.

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

Cfr. https://github.com/RichardHightower/boon Boon ha un parser PLIST (ASCII) e un parser JSON.

Il parser JSON è il parser JSON Java più veloce che io conosca.

Verificato in modo indipendente dai tizi Gatling prestazioni.

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

Ha il parser JSON più veloce per flussi, lettori, byte [], char [], CharSequence (StringBuilder, CharacterBuffer) e String.

Vedere di più parametri di riferimento a:

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

 -2
Author: RickHigh, 2013-12-14 02:48:34