Conversion d'une chaîne compatible ISO 8601 en java.util.Date


J'essaie de convertir une chaîne formatée ISO 8601 en java.util.Date.

J'ai trouvé le modèle yyyy-MM-dd'T'HH:mm:ssZ conforme à ISO8601 s'il est utilisé avec un paramètre régional (comparer l'échantillon).

Cependant, en utilisant le java.text.SimpleDateFormat, je ne peux pas convertir la chaîne correctement formatée 2010-01-01T12:00:00+01:00. Je dois d'abord le convertir en 2010-01-01T12:00:00+0100, sans les deux-points.

Donc, la solution actuelle est

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

Ce qui n'est évidemment pas si agréable. Est-ce que je manque quelque chose ou y a-t-il un meilleur la solution?


Réponse

Grâce au commentaire de JuanZe, j'ai trouvé le Joda-Timemagique, il est aussi décrit ici.

Donc, la solution est

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

Ou plus simplement, utilisez l'analyseur par défaut via le constructeur:

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

Pour moi, c'est sympa.

Author: wittich, 2010-02-04

25 answers

Malheureusement, les formats de fuseau horaire disponibles pour SimpleDateFormat (Java 6 et versions antérieures) ne sont pas conformes à ISO 8601 . SimpleDateFormat comprend les chaînes de fuseau horaire comme " GMT + 01: 00 "ou" +0100", ce dernier selon RFC # 822.

Même si Java 7 a ajouté la prise en charge des descripteurs de fuseau horaire selon ISO 8601, SimpleDateFormat n'est toujours pas en mesure d'analyser correctement une chaîne de date complète, car il n'a pas de prise en charge des parties facultatives.

Reformatage votre chaîne d'entrée utilisant regexp est certainement une possibilité, mais les règles de remplacement ne sont pas aussi simples que dans votre question:

  • Certains fuseaux horaires ne sont pas des heures complètes UTC, donc la chaîne ne se termine pas nécessairement par ":00".
  • ISO8601 ne permet que le nombre d'heures à inclure dans le fuseau horaire, donc "+01 "est équivalent à"+01:00 "
  • ISO8601 permet l'utilisation de "Z" pour indiquer UTC au lieu de "+00:00".

La solution la plus simple est peut-être pour utiliser le convertisseur de type de données dans JAXB, car JAXB doit être capable d'analyser la chaîne de date ISO8601 selon la spécification de schéma XML. javax.xml.bind.DatatypeConverter.parseDateTime("2010-01-01T12:00:00Z") vous donnera un objet Calendar et vous pouvez simplement utiliser getTime() dessus, si vous avez besoin d'un objet Date.

Vous pourriez probablement utiliser Joda-Time aussi, mais je ne sais pas pourquoi vous devriez vous embêter avec cela.

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

D'accord, cette question est déjà répondue, mais je laisserai tomber ma réponse quand même. Il pourrait aider quelqu'un.

J'ai cherché une solution pour Android (API 7).

  • Joda était hors de question - il est énorme et souffre d'une initialisation lente. Cela semblait également une exagération majeure à cet effet particulier.
  • Les réponses impliquant javax.xml ne fonctionneront pas sur Android API 7.

A fini par implémenter cette classe simple. Il ne couvre que les plus courants form de chaînes ISO 8601, mais cela devrait suffire dans certains cas (lorsque vous êtes tout à fait sûr que l'entrée sera au format this).

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

Note de performance: J'instancie nouveau SimpleDateFormat à chaque fois comme moyen d'éviterun bug dans Android 2.1. Si vous êtes aussi étonné que moi, voyez cette énigme. Pour les autres moteurs Java, vous pouvez mettre en cache l'instance dans un champ statique privé (en utilisant ThreadLocal, pour être thread safe).

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

La façon dont est béni par la documentation 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);

Vous pouvez trouver plus d'exemples dans la section Exemples à SimpleDateFormat javadoc.

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

Java.temps

Le java.time API (intégré dans Java 8 et versions ultérieures), rend cela un peu plus facile.

Si vous savez que l'entrée est dans UTC, comme le Z (pour Zulu) à la fin, le Instant la classe peut analyser.

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

Si votre entrée peut être une autre valeur offset-from-UTC plutôt que UTC indiquée par le Z (Zulu) à la fin, utilisez le OffsetDateTime classe à analyser.

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

Puis extraire un Instant, et le convertir en un java.util.Date en appelant 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 bibliothèque Jackson-databinda également ISO8601DateFormat classqui fait cela (implémentation réelle dans 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" )

Utilisation de java.temps

Le nouveau java.le paquet time en Java 8 et plus tard a été inspiré par Joda-Time.

Le OffsetDateTime classe représente un moment sur la timeline avec un offset-de-UTC, mais pas un fuseau horaire.

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

Appeler toString génère une chaîne au format ISO 8601 standard:

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

Pour voir la même valeur à travers l'objectif de l'UTC, extrayez un Instant ou ajustez décalage de +01:00 à 00:00.

Instant instant = odt.toInstant();  

...ou...

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

Ajustez dans un fuseau horaire si vous le souhaitez. Unfuseau horaire est un historique des valeurs deoffset-from-UTC pour une région, avec un ensemble de règles pour gérer les anomalies telles que l'heure d'été (DST). Appliquez donc un fuseau horaire plutôt qu'un simple décalage chaque fois que possible.

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

À propos de java.temps

Le java.temps framework est intégré à Java 8 et versions ultérieures. Ils les classes de supplanter le gênants vieux héritage date-heure classes telles que java.util.Date, Calendar, & SimpleDateFormat.

Le Joda-Temps le projet, maintenant en mode de maintenance, conseille la migration vers le java.temps classes.

Pour en savoir plus, voir le Tutoriel Oracle. Et rechercher Stack Overflow pour de nombreux exemples et explications. La spécification est JSR 310.

Vous pouvez échange java.temps objets directement avec votre base de données. Utilisez un pilote JDBC compatible avec JDBC 4.2 ou version ultérieure. Pas besoin de chaînes, pas besoin de classes java.sql.*.

Où obtenir le java.les classes de temps?

  • Java SE 8, Java SE 9, Java SE 10, et plus tard
    • Intégré.
    • Partie de l'API Java standard avec un application.
    • Java 9 ajoute quelques fonctionnalités et corrections mineures.
  • Java SE 6 et Java SE 7
    • Une grande partie du java.fonction temps est de retour, porté à Java 6 et 7ThreeTen-Backport.
  • Android
    • Versions ultérieures des implémentations de bundle Android du java.les classes de temps.
    • Pour Android plus tôt (Troisabp le projet adapteThreeTen-Backport (mentionné ci-dessus). Voir Comment utiliser ThreeTenABP....

Le Trois-Extra le projet étend java.temps avec des cours supplémentaires. Ce projet est un terrain d'essai pour d'éventuels ajouts futurs à java.temps. Vous pouvez trouver quelques classes utiles ici comme Interval, YearWeek, YearQuarter, et plus.


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

Pour Java version 7

Vous pouvez suivre la documentation Oracle: http://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html

X-est utilisé pour le fuseau horaire 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 solution DatatypeConverter ne fonctionne pas dans toutes les machines virtuelles. Les travaux suivants pour moi:

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

J'ai trouvé que joda ne fonctionne pas hors de la boîte (spécifiquement pour l'exemple que j'ai donné ci-dessus avec le fuseau horaire sur une date, qui devrait être valide)

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

Je pense que nous devrions utiliser

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

Pour la date 2010-01-01T12:00:00Z

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

Une autre façon très simple d'analyser les horodatages ISO8601 consiste à utiliser 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.temps

Notez qu'en Java 8, vous pouvez utiliser le java.temps.ZonedDateTime classe et sa méthode statique parse(CharSequence text).

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

J'ai fait face au même problème et l'ai résolu par le code suivant .

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

Plus tôt, j'utilisais SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.getDefault());

, Mais plus tard, j'ai trouvé la cause principale de l'exception était la yyyy-MM-dd'T'HH:mm:ss.SSSZ ,

J'ai donc utilisé

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

Cela a bien fonctionné pour moi .

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

Vous pouvez également utiliser la classe suivante -

org.springframework.extensions.surf.util.ISO8601DateFormat


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

Lien vers la hiérarchie Java Doc - Pour l'organisation des paquets.springframework.extension.surf.maven.plugin.util

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

Apache Jackrabbit utilise le format ISO 8601 pour les dates persistantes, et il existe une classe d'aide pour les analyser:

Org.Apache.jackrabbit.util.ISO8601

Est Livré avec jackrabbit-jcr-commons.

 3
Author: Alexander Klimetschek, 2013-04-22 10:28:33

Comme d'autres l'ont mentionné, Android n'a pas un bon moyen de prendre en charge l'analyse/le formatage des dates ISO 8601 à l'aide des classes incluses dans le SDK. J'ai écrit ce code plusieurs fois, j'ai donc finalement créé un essentiel qui inclut une classe DateUtils qui prend en charge le formatage et l'analyse des dates ISO 8601 et RFC 1123. L'essentiel comprend également un cas de test montrant ce qu'il prend en charge.

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

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

La solution de contournement pour Java 7 + utilise SimpleDateFormat:
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX", Locale.US);

Ce code peut analyser le format ISO8601 comme:

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

Mais sur Java6, SimpleDateFormat ne comprend pas le caractère X et lancera
IllegalArgumentException: Unknown pattern character 'X'
Nous devons normaliser la date ISO8601 au format lisible en Java 6 avec 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);
    }
}

Méthode ci-dessus pour remplacer [Z avec +0000] ou [+01:00 avec +0100] quand l'erreur se produit dans Java 6 (vous pouvez détecter Java version et remplacer try / catch par l'instruction if).

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

SimpleDateFormat pour JAVA 1.7 a un modèle cool pour le format ISO 8601.

La Classe SimpleDateFormat

Voici ce que j'ai fait:

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 a une douzaine de façons différentes d'analyser une date-heure, comme le démontrent les excellentes réponses ici. Mais étonnamment, aucune des classes de temps de Java n'implémente pleinement ISO 8601!

Avec Java 8, je recommanderais:

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

Qui gérera des exemples à la fois en UTC et avec un décalage, comme "2017-09-13T10:36: 40Z" ou "2017-09-13T10:36:40+01:00". Il fera pour la plupart des cas d'utilisation.

Mais il ne gérera pas d'exemples comme "2017-09-13T10:36:40+01", qui est un ISO 8601 valide date-heure.
Il ne traitera pas non plus uniquement la date, par exemple "2017-09-13".

Si vous devez les gérer, je suggère d'utiliser d'abord une expression régulière pour renifler la syntaxe.

Il y a une belle liste d'exemples ISO 8601 ici avec beaucoup de cas d'angle: https://www.myintervals.com/blog/2009/05/20/iso-8601-date-validation-that-doesnt-suck / Je ne connais aucune classe Java qui pourrait les gérer toutes.

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

Utiliser une chaîne comme LocalDate.parse(((String) data.get("d_iso8601")),DateTimeFormatter.ISO_DATE)

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

J'avais un besoin similaire: je devais pouvoir analyser n'importe quelle date compatible ISO8601 sans connaître le format exact à l'avance, et je voulais une solution légère qui fonctionnerait également sur Android.

Quand j'ai googlé mes besoins, je suis tombé sur cette question et j'ai remarqué qu'AFAIU, aucune réponse ne correspondait complètement à mes besoins. J'ai donc développé jISO8601 et l'ai poussé sur maven central.

Il suffit d'ajouter dans vous pom.xml:

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

Et puis vous êtes prêt à partir:

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

Espère qu'il aider.

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

Pour simplement formater une date comme celle-ci, ce qui suit a fonctionné pour moi dans une application basée sur Java 6. Il y a une classe DateFormat JacksonThymeleafISO8601DateFormat dans le projet thymeleaf qui insère les deux points manquants:

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

Je l'ai utilisé pour la compatibilité du format de date ECMAScript.

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

Faites-le comme ceci:

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

}

Voici la sortie:

Mer Oct 19 15: 15: 36 CST 2016

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

Fonction de base Courtoisie : @wrygiel.

Cette fonction peut convertir le format ISO8601 en Java Date qui peut gérer les valeurs de décalage. Selon la définition de la norme ISO 8601 , le décalage peut être mentionné dans différents formats.

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

Cette classe a des méthodes statiques pour convertir

  • ISO8601 chaîne à date(fuseau horaire local) objet
  • Date de la chaîne ISO8601
  • L'heure d'été est automatiquement calc

Échantillon ISO8601 Chaînes

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

Cela semblait fonctionner le mieux pour moi:

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


}

J'avais besoin de convertir les chaînes de date JavaScript en Java. J'ai trouvé que ce qui précède fonctionne avec la recommandation. Il y avait quelques exemples utilisant SimpleDateFormat qui étaient proches mais ils ne semblaient pas être le sous-ensemble tel que recommandé par:

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

Et pris en charge par les chaînes PLIST et JavaScript et ce qui est ce dont j'avais besoin.

Cela semble être la forme la plus courante d'ISO8601 chaîne là-bas, et un bon sous-ensemble.

Les exemples qu'ils donnent sont:

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.

J'ai aussi une version rapide:

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

Je ne l'ai pas comparé, mais je suppose que ce sera assez rapide. Il semble fonctionner. :)

@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

Je pense que ce que beaucoup de gens veulent faire, c'est analyser les chaînes de date JSON. Il y a de bonnes chances si vous arrivez sur cette page que vous souhaitiez convertir une date JSON JavaScript en une date Java.

Pour montrer à quoi ressemble une chaîne de date 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 chaîne de date JSON est 2013-12-14T01:55: 33.412 Z.

Les dates ne sont pas couvertes par les spécifications JSON, mais ce qui précède est un format ISO 8601 très spécifique, tandis que ISO_8601 est beaucoup plus grand et c'est un simple sous-ensemble bien qu'un très important.

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

Comme il se trouve, j'ai écrit un analyseur JSON et un analyseur PLIST qui utilisent tous deux ISO-8601 mais pas les mêmes 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);
}

J'ai écrit deux façons de le faire pour mon projet. Un standard, un rapide.

Encore une fois, la chaîne de date JSON est une implémentation très spécifique d'ISO 8601....

(j'ai posté l'autre dans le autre réponse qui devrait fonctionner pour les dates PLIST, qui sont un format ISO 8601 différent).

La date JSON est la suivante:

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


}

Les fichiers PLIST (ASCII non GNUNext) utilisent également ISO 8601 mais pas de milisecondes donc... toutes les dates ISO-8601 ne sont pas les mêmes. (Au moins, je n'en ai pas encore trouvé un qui utilise milis et l'analyseur que j'ai vu ignorer complètement le fuseau horaire OMG).

Maintenant, pour la version rapide (vous pouvez le trouver dans Boon).

public static Date fromJsonDate( String string ) {

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

}

Notez cette réflexion.toCharArray utilise dangereux s'il est disponible, mais la valeur par défaut est string.toCharArray si pas.

(Vous pouvez le retirer de l'exemple en remplaçant la réflexion.toCharArray (chaîne) avec chaîne.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 est implémenté comme suit:

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

De toute façon... ma conjecture est que très peu de gens qui viennent ici.. vous cherchez peut-être la chaîne de date JSON et bien qu'il s'agisse d'une date ISO-8601, c'est une date très spécifique qui nécessite une analyse très spécifique.

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

Voir https://github.com/RichardHightower/boon Boon a un analyseur PLIST (ASCII) et un analyseur JSON.

L'analyseur JSON est l'analyseur JSON Java le plus rapide que je connaisse.

Vérifié de manière indépendante par les 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

Il a l'analyseur JSON le plus rapide pour les flux, les lecteurs, les octets [], char [], CharSequence (StringBuilder, CharacterBuffer) et String.

Voir plus indices de référence à:

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

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