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

668

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

J'ai trouvé que le modèle yyyy-MM-dd'T'HH:mm:ssZétait conforme à ISO8601 s'il était 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.

Ainsi, 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 gentil. Suis-je en train de manquer quelque chose ou y at-il une meilleure solution?


Réponse

Grâce au commentaire de JuanZe, j'ai trouvé la magie Joda-Time , elle est également décrite 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.

Ice09
la source
243
Soyez prêt à recevoir beaucoup de réponses "Use JodaTime" ...
JuanZe
3
@ Ice09: Si la documentation de l'API pour DateTimeFormat est correcte (la documentation JoDa peut être trompeuse, erronée ou incomplète), le modèle que vous avez utilisé dans votre propre "réponse" n'est pas compatible avec ISO8601.
jarnbjo
21
Je ne sais pas quand cela a été ajouté, mais le «X» semble résoudre ce problème dans SimpleDateFormat. Le modèle "aaaa-MM-jj'T'HH: mm: ssX" analyse correctement l'exemple de la question.
mlohbihler
12
Le «X» est disponible depuis Java 7.
Lars Grammel
3
Java 8 vous facilite la tâche! Il y a un joyau caché par Adam dans les réponses ci-dessous: stackoverflow.com/a/27479533/1262901
Fabian Keller

Réponses:

477

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 la 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 ne prend pas en charge les parties facultatives.

Le reformatage de votre chaîne d'entrée à l'aide de l'expression rationnelle 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 hors UTC , donc la chaîne ne se termine pas nécessairement par ": 00".
  • ISO8601 autorise uniquement le nombre d'heures à inclure dans le fuseau horaire, donc "+01" équivaut à "+01: 00"
  • ISO8601 autorise l'utilisation de "Z" pour indiquer UTC au lieu de "+00: 00".

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

Vous pourriez probablement aussi utiliser Joda-Time , mais je ne sais pas pourquoi vous devriez vous en préoccuper.

jarnbjo
la source
18
La solution JAXB est une approche vraiment créative! Ça marche aussi, je l'ai testé avec mon échantillon. Cependant, pour quiconque est confronté au problème et autorisé à utiliser JodaTime, je conseillerais de l'utiliser, car il semble plus naturel. Mais votre solution ne nécessite pas de bibliothèques supplémentaires (au moins avec Java 6).
Ice09
36
Voici l'inverse: Calendar c = GregorianCalendar.getInstance (); c.setTime (aDate); return javax.xml.bind.DatatypeConverter.printDateTime (c);
Alexander Ljungberg
4
En fait, ce n'est pas si simple b / c que vous devez initialiser le jaxb datatypeConverter. J'ai fini par utiliser DatatypeFactory moi-même comme DataTypeConverterImpl l'a fait en interne. Quel mal de tête.
gtrak
3
@Simon: Non, les fuseaux horaires ne sont bien sûr pas ignorés. Vous devez faire quelque chose de mal. Si vous tapez plus de quelques caractères et nous dites ce que vous faites vraiment, quelqu'un peut vous expliquer quoi.
jarnbjo
4
@jarnbjo vous êtes la première et la seule personne que j'ai rencontrée qui préfère les classes de date java standard, pré 1.8, à joda-time. Je trouve le joda-time une joie littérale à utiliser, surtout par rapport à l'api standard qui est une abomination.
NimChimpsky
245

La voie qui est bénie 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 sur javadoc SimpleDateFormat .

UPD 13/02/2020: Il existe une toute nouvelle façon de le faire dans Java 8

Antonio
la source
7
Votre réponse m'a aidé à convertir l'ISODate de MongoDB en date locale. Cordialement.
Blue Sky
9
@ b.long Java a ajouté plus qu'une constante pour de tels formats conformes à ISO 8601. Java a obtenu un tout nouveau cadre pour le travail date-heure qui inclut la prise en charge intégrée par défaut de ces formats. Voir le nouveau java.timecadre de Java 8, inspiré de Joda-Time , remplaçant les classes java.util.Date, .Calendar et SimpleDateFormat gênantes.
Basil Bourque
2
Cela ne signifie-t-il pas que vous devez connaître le format de la date à l'avance? Et si vous devez accepter string1et string2ne savez pas ce que vous obtiendrez.
Timmmm
16
'Z' doit être entre guillemets
kervin
7
@kervin Si le Z est entre guillemets, le formateur ne cherchera-t-il pas spécifiquement le caractère Z, pas toutes les chaînes de décalage qu'il peut représenter? Il semble que citer Z ne fonctionnerait que par coïncidence, si vos chaînes de date se trouvaient en UTC.
spaaarky21
201

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

Je cherchais une solution pour Android (API 7).

  • Joda était hors de question - il est énorme et souffre d'une lente initialisation. Cela semblait également une surpuissance majeure à cette fin particulière.
  • Les réponses impliquant javax.xmlne fonctionneront pas sur Android API 7.

J'ai fini par implémenter cette classe simple. Il ne couvre que la forme la plus courante des chaînes ISO 8601, mais cela devrait suffire dans certains cas (lorsque vous êtes certain que l'entrée sera dans ce format).

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 sur les performances: j'instancie chaque fois SimpleDateFormat pour éviter un bogue dans Android 2.1. Si vous êtes aussi étonné que moi, voyez cette énigme . Pour les autres moteurs Java, vous pouvez mettre l'instance en cache dans un champ statique privé (en utilisant ThreadLocal, pour être sûr pour les threads).

wrygiel
la source
2
Peut-être que cela aurait dû être une question à part entière, avec sa propre réponse?
Thorbear
5
C'était la première page sur laquelle je me suis penchée lorsque je cherchais la réponse, donc elle semblait appropriée. Pour la plupart des développeurs Java, Android n'est pas exactement Java. Cependant, dans la plupart des cas, l'un fonctionne de la même manière que l'autre, donc de nombreux développeurs Android rechercheront "java" lorsqu'ils le chercheront.
wrygiel
1
Notez que cela ne tient pas compte de la résolution en millisecondes. C'est facile à ajouter.
Sky Kelsey
6
j'ai dû ajouter .SSS pendant quelques secondes mais fonctionne très bien. Pourquoi faites-vous s = s.substring(0, 22) + s.substring(23);- je ne vois pas l'intérêt de cela
Dori
1
input = input.replaceAll ("[Zz]", "+0000"); fonctionnera aussi et l'opération de sous-chaîne peut être évitée.
Javanator
115

java.time

L' API java.time (intégrée à Java 8 et versions ultérieures) rend cela un peu plus facile.

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

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

Si votre entrée peut être une autre valeur de décalage par rapport à UTC plutôt que UTC indiquée par le Z(Zulu) à la fin, utilisez la OffsetDateTimeclasse pour analyser.

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

Ensuite, extrayez un Instantet convertissez-le java.util.Dateen appelant from.

Instant instant = odt.toInstant();  // Instant is always in UTC.
java.util.Date date = java.util.Date.from( instant );
Adam
la source
8
Cette réponse travaille trop dur. Un java.util.Date par définition n'a pas de fuseau horaire. Donc pas besoin de tout ce code lié au fuseau horaire: les LocalDateTimeet ZoneIdet atZone. Ce simple paquebot fera:java.util.Date date = Date.from( ZonedDateTime.parse( "2014-12-12T10:39:40Z" ).toInstant() );
Basil Bourque
5
@BasilBourque C'est inutilement compliqué: Date.from(Instant.parse("2014-12-12T10:39:40Z" ));c'est suffisant.
assylias
3
@assylias vous avez raison mais cela ne fonctionnera que lorsque la chaîne de date est la zone UTC, ISO8601 autorise tout fuseau horaire ...
Adam
2
@Adam My bad - je n'avais pas réalisé que la question était plus générale que votre exemple. En guise de commentaire secondaire, un OffsetDateTimesuffirait pour analyser ISO8601 (qui ne contient pas d'informations sur le fuseau horaire mais uniquement un décalage).
assylias
1
@assylias Merci pour votre commentaire sur le fait de laisser Instantl'analyse. Bien qu'elle ne soit pas suffisante pour cette question particulière, il s'agit d'une distinction importante qui mérite d'être soulignée. J'ai donc ajouté un deuxième exemple de code. Oups, je viens de remarquer que ce n'est pas à l'origine ma réponse; J'espère qu'Adam l'approuve.
Basil Bourque
67

La bibliothèque Jackson-databind a également la classe ISO8601DateFormat qui fait cela (implémentation réelle dans ISO8601Utils .

ISO8601DateFormat df = new ISO8601DateFormat();
Date d = df.parse("2010-07-28T22:25:51Z");
david_p
la source
Il ne parvient pas à analyser cette date: 2015-08-11T13:10:00. Je comprends String index out of range: 19. En regardant le code, il semble qu'il nécessite la spécification des millisecondes et du fuseau horaire. Ceux-ci devraient être facultatifs.
Timmmm
2
Pour citer la documentation, le format Parse est: [yyyy-MM-dd|yyyyMMdd][T(hh:mm[:ss[.sss]]|hhmm[ss[.sss]])]?[Z|[+-]hh:mm]]. En d'autres termes, les millisecondes sont facultatives mais le fuseau horaire est obligatoire.
david_p
2
Ah oui en fait on dirait que tu as raison. Pourtant, je suis presque sûr que ISO8601 vous permet d'omettre le fuseau horaire, donc c'est toujours faux. JodaTime fonctionne cependant:new DateTime("2015-08-11T13:10:00").toDate()
Timmmm
3
Cette classe est désormais obsolète, la nouvelle est StdDateFormat. Sinon, cela fonctionne de la même manière.
JohnEye
51

tl; dr

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

Utilisation de java.time

Le nouveau package java.time dans Java 8 et versions ultérieures a été inspiré par Joda-Time.

La OffsetDateTimeclasse représente un moment sur la timeline avec un décalage par rapport à UTC mais pas un fuseau horaire.

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

L'appel toStringgénère une chaîne au format standard ISO 8601:

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

Pour voir la même valeur à travers l'objectif de l'UTC, extrayez un Instantou ajustez le 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. Un fuseau horaire est un historique des valeurs de décalage par rapport à 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 dans la mesure du possible.

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

À propos de java.time

Le framework java.time est intégré à Java 8 et versions ultérieures. Ces classes supplantent les anciens gênants hérités des classes date-heure tels que java.util.Date, Calendar, et SimpleDateFormat.

Le projet Joda-Time , désormais en mode maintenance , conseille la migration vers les classes java.time .

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

Vous pouvez échanger des objets java.time 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 java.sql.*cours.

Où obtenir les classes java.time?

  • Java SE 8 , Java SE 9 , Java SE 10 et versions ultérieures
    • Intégré.
    • Une partie de l'API Java standard avec une implémentation groupée.
    • Java 9 ajoute quelques fonctionnalités et correctifs mineurs.
  • Java SE 6 et Java SE 7
    • Une grande partie de la fonctionnalité java.time est rétroportée vers Java 6 et 7 dans ThreeTen-Backport .
  • Android
    • Versions ultérieures des implémentations de bundles Android des classes java.time.
    • Pour les versions antérieures d'Android (<26), le projet ThreeTenABP adapte ThreeTen-Backport (mentionné ci-dessus). Voir Comment utiliser ThreeTenABP… .

Le projet ThreeTen-Extra étend java.time avec des classes supplémentaires. Ce projet est un terrain d'essai pour de futurs ajouts possibles à java.time. Vous trouverez peut - être des classes utiles ici, comme Interval, YearWeek, YearQuarteret plus .


Basil Bourque
la source
27

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);
d.danailov
la source
Cela signifie que le fuseau horaire est requis . Selon ISO 8601, il est facultatif. Tout comme les secondes, etc. Donc, cela n'analyse qu'un sous-ensemble spécifique d'ISO 8601.
Timmmm
1
Fonctionne très bien avec Java 1.8
Thiago Pereira
20

La solution DatatypeConverter ne fonctionne pas sur toutes les machines virtuelles. Ce qui suit fonctionne 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 (en particulier pour l'exemple que j'ai donné ci-dessus avec le fuseau horaire à une date, qui devrait être valide)

James Scriven
la source
15

Je pense que nous devrions utiliser

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

pour Date 2010-01-01T12:00:00Z

Toby
la source
5
Pourquoi est-ce une meilleure réponse que les autres, y compris la réponse acceptée avec 76 votes positifs?
Erick Robertson
3
@ErickRobertson: C'est simple, prêt à l'emploi, flexible, sans conversions, et la plupart des gens ne se soucient pas des fuseaux horaires.
TWiStErRob
7
pas grand chose à travailler avec les temps si vous ne vous souciez pas des fuseaux horaires!
Dori
16
Cela IGNORE complètement le fuseau horaire. J'utilisais cela jusqu'à ce que je réalise que cela se produisait, alors je suis passé à JodaTime.
Joshua Pinter
3
Jeter le fuseau horaire entraînera simplement des erreurs à un moment donné.
Bart van Kuik
11

À partir de Java 8, il existe une toute nouvelle façon officiellement prise en charge pour ce faire:

    String s = "2020-02-13T18:51:09.840Z";
    TemporalAccessor ta = DateTimeFormatter.ISO_INSTANT.parse(s);
    Instant i = Instant.from(ta);
    Date d = Date.from(i);
Antonio
la source
2
Si la chaîne est au format instantané, avec la fin Zcomme décalage, nous n'avons pas besoin de le spécifier explicitement. Justement Instant i = Instant.parse(s);. La chaîne dans la question avait +01:00, auquel cas DateTimeFormatter.ISO_INSTANTne fonctionne pas (du moins pas sur mon Java 11).
Ole VV
3
@ OleV.V. Vous pouvez utiliser ISO_OFFSET_DATE_TIMEpour formater les dates avec des décalages, comme +01:00( docs.oracle.com/javase/8/docs/api/java/time/format/… )
Lucas Basquerotto
1
C'est vrai, @LucasBasquerotto. Sans mentionner explicitement ce formateur, les réponses d'Adam et de Basil Bourque font déjà quelque chose de similaire.
Ole VV
Cela ne peut pas analyser "2020-06-01T14: 34: 00-05: 00" qui est une chaîne produite par la méthode toISOString () de Javascript.
Jose Solorzano
10

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());
  }
}
tmandry
la source
6

java.time

Notez que dans Java 8, vous pouvez utiliser la classe java.time.ZonedDateTime et sa parse(CharSequence text)méthode statique .

Martin Rust
la source
Les chaînes d'entrée dans la question n'ont qu'un décalage par rapport à UTC, pas un fuseau horaire complet. Donc, Instantet ZonedDateTimesont appropriés ici, non ZonedDateTime.
Basil Bourque
6

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, SimpleDateFormatne comprend pas le Xcaractère et jettera
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 [ Zavec +0000] ou [ +01:00avec +0100] lorsqu'une erreur se produit dans Java 6 (vous pouvez détecter la version Java et remplacer l'instruction try / catch avec if).

Khang .NT
la source
Non, les anciennes classes de date-heure gênantes telles que Dateet SimpleDateFormatsont mal conçues, déroutantes et imparfaites. Ils sont désormais hérités, supplantés par les classes java.time intégrées à Java 8 et versions ultérieures. Pour Java 6 et Java 7, une grande partie de la fonctionnalité java.time est rétroportée dans le projet ThreeTen-Backport . Il vaut bien mieux ajouter cette bibliothèque à votre application que d'utiliser ces classes héritées. Solution monoligne en java.time:OffsetDateTime.parse( "2010-01-01T12:00:00+01:00" )
Basil Bourque
5

J'ai rencontré le 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é que la principale cause de l'exception était le 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.

Abhay Kumar
la source
Juste ce dont j'avais besoin sans avoir à utiliser joda-time, XML api ou quoi que ce soit d'autre. Juste le bon schéma.
Philippe Gioseffi
4

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 horaires de Java n'implémente complètement ISO 8601!

Avec Java 8, je recommanderais:

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

Cela 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 l'affaire pour la plupart des cas d'utilisation.

Mais il ne prendra pas en charge des exemples tels que "2017-09-13T10: 36: 40 + 01", qui est une date-heure ISO 8601 valide.
Il ne traitera pas uniquement la date, par exemple "13/09/2017".

Si vous devez les gérer, je suggère d'utiliser d'abord une expression régulière pour flairer 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/ I'm pas au courant de toute classe Java qui pourrait faire face à tous.

Daniel Winterstein
la source
OffsetDateTimefera et correspond conceptuellement à une date-heure avec un décalage mieux.
Ole VV
Salut @ OleV.V. Merci pour la suggestion. Malheureusement non: OffsetDateTime.parse () lèvera une exception pour plusieurs chaînes ISO 8601 valides, par exemple "2017-09-13T10: 36: 40 + 01" ou "2017-09-13"
Daniel Winterstein
Je voulais seulement dire que OffsetDateTimegère les exemples que vous manipulez avec ZonedDateTime. Je crois qu'il ne gère aucun des exemples qui ZonedDateTimene le font pas. En ce sens, ce n'est pas une amélioration (mais pas pire non plus). Désolé, je n'étais pas parfaitement clair.
Ole VV
1
Cela devrait être la réponse acceptée en 2020, compte tenu de l'état des choses.
slashCoder
3

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

org.apache.jackrabbit.util.ISO8601

Livré avec jackrabbit-jcr-commons .

Alexander Klimetschek
la source
Bien que le sous-ensemble de Jackrabbit puisse fonctionner, il est plus logique d'utiliser une bibliothèque entièrement conçue à cet effet. En Java, cela signifie soit Joda-Time ou java.time.
Basil Bourque
3

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 Gist qui comprend une classe DateUtils qui prend en charge le formatage et l'analyse des dates ISO 8601 et RFC 1123. Le Gist comprend également un cas de test montrant ce qu'il prend en charge.

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

Matt Accola
la source
2

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

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());
Eesha
la source
2
Zdans la chaîne de format n'est pas le fuseau horaire ISO 8601, vous devez utiliser X(ou XXou XXX) si vous voulez un fuseau horaire ISO 8601
Vojta
d est de type String
Tim Child
1

Fais-le comme ça:

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.19 octobre 15:15:36 CST 2016

yinhaomin
la source
1

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

Stepan
la source
1

Je suis surpris que pas une seule bibliothèque java ne prenne en charge tous les formats de date ISO 8601 selon https://en.wikipedia.org/wiki/ISO_8601 . Joda DateTime supportait la plupart d'entre eux, mais pas tous et j'ai donc ajouté une logique personnalisée pour les gérer tous. Voici ma mise en œuvre.

import java.text.ParseException;
import java.util.Date;

import org.apache.commons.lang3.time.DateUtils;
import org.joda.time.DateTime;

public class ISO8601DateUtils {
	
	/**
	 * It parses all the date time formats from https://en.wikipedia.org/wiki/ISO_8601 and returns Joda DateTime.
	 * Zoda DateTime does not support dates of format 20190531T160233Z, and hence added custom logic to handle this using SimpleDateFormat.
	 * @param dateTimeString ISO 8601 date time string
	 * @return
	 */
	public static DateTime parse(String dateTimeString) {
		try {
			return new DateTime( dateTimeString );
		} catch(Exception e) {
			try {
				Date dateTime = DateUtils.parseDate(dateTimeString, JODA_NOT_SUPPORTED_ISO_DATES);
				return new DateTime(dateTime.getTime());
			} catch (ParseException e1) {
				throw new RuntimeException(String.format("Date %s could not be parsed to ISO date", dateTimeString));
			}
		}
	}
  
  	private static String[] JODA_NOT_SUPPORTED_ISO_DATES = new String[] {
			// upto millis
			"yyyyMMdd'T'HHmmssSSS'Z'",
			"yyyyMMdd'T'HHmmssSSSZ",
			"yyyyMMdd'T'HHmmssSSSXXX",
			
			"yyyy-MM-dd'T'HHmmssSSS'Z'",
			"yyyy-MM-dd'T'HHmmssSSSZ",
			"yyyy-MM-dd'T'HHmmssSSSXXX",
			
			// upto seconds
			"yyyyMMdd'T'HHmmss'Z'",
			"yyyyMMdd'T'HHmmssZ",
			"yyyyMMdd'T'HHmmssXXX",
			
			"yyyy-MM-dd'T'HHmmss'Z'", 
			"yyyy-MM-dd'T'HHmmssZ",
			"yyyy-MM-dd'T'HHmmssXXX",
			
			// upto minutes
			"yyyyMMdd'T'HHmm'Z'",
			"yyyyMMdd'T'HHmmZ",
			"yyyyMMdd'T'HHmmXXX",

			"yyyy-MM-dd'T'HHmm'Z'",
			"yyyy-MM-dd'T'HHmmZ",
			"yyyy-MM-dd'T'HHmmXXX",
			
			//upto hours is already supported by Joda DateTime
	};
}

raok1997
la source
1

Un petit test qui montre comment analyser une date dans ISO8601 et que LocalDateTime ne gère pas les DST.

 @Test
    public void shouldHandleDaylightSavingTimes() throws ParseException {

        //ISO8601 UTC date format
        SimpleDateFormat utcFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");

        // 1 hour of difference between 2 dates in UTC happening at the Daylight Saving Time
        Date d1 = utcFormat.parse("2019-10-27T00:30:00.000Z");
        Date d2 = utcFormat.parse("2019-10-27T01:30:00.000Z");

        //Date 2 is before date 2
        Assert.assertTrue(d1.getTime() < d2.getTime());
        // And there is 1 hour difference between the 2 dates
        Assert.assertEquals(1000*60*60, d2.getTime() - d1.getTime());

        //Print the dates in local time
        SimpleDateFormat localFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm z Z", Locale.forLanguageTag("fr_CH"));
        localFormat.setTimeZone(TimeZone.getTimeZone("Europe/Zurich"));

        //Both dates are at 02h30 local time (because of DST), but one is CEST +0200 and the other CET +0100 (clock goes backwards)
        Assert.assertEquals("2019-10-27 02:30 CEST +0200", localFormat.format(d1));
        Assert.assertEquals("2019-10-27 02:30 CET +0100", localFormat.format(d2));

        //Small test that shows that LocalDateTime does not handle DST (and should not be used for storing timeseries data)
        LocalDateTime ld1 = LocalDateTime.ofInstant(d1.toInstant(), ZoneId.of("Europe/Zurich"));
        LocalDateTime ld2 = LocalDateTime.ofInstant(d2.toInstant(), ZoneId.of("Europe/Zurich"));

        //Note that a localdatetime does not handle DST, therefore the 2 dates are the same
        Assert.assertEquals(ld1, ld2);

        //They both have the following local values
        Assert.assertEquals(2019, ld1.getYear());
        Assert.assertEquals(27, ld1.getDayOfMonth());
        Assert.assertEquals(10, ld1.getMonthValue());
        Assert.assertEquals(2, ld1.getHour());
        Assert.assertEquals(30, ld1.getMinute());
        Assert.assertEquals(0, ld1.getSecond());

    }
ddtxra
la source
3
Pour info, les classes de date-heure terriblement gênantes telles que java.util.Date, java.util.Calendaret java.text.SimpleDateFormatsont désormais héritées , supplantées par les classes java.time intégrées à Java 8 et versions ultérieures. Voir Tutoriel par Oracle .
Basil Bourque
Vous avez raison qui LocalDateTimene gère pas l'heure d'été (DST) car elle ne gère pas du tout un fuseau horaire. Pour cela, nous avons besoin ZonedDateTime. Proposer Dateet SimpleDateFormatest - à mon humble avis mauvais.
Ole VV
1
En effet, ZonedDateTime fonctionne. Et java.time.Instant est également une bonne alternative pour gérer l'heure d'été. Je sais que java.util.Date est obsolète et ne devrait pas être utilisé, mais je répondais juste à la question d'origine: comment convertir une chaîne dans 8601 en java.util.date ....
ddtxra
0

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

Lorsque j'ai recherché mes besoins sur Google, je suis tombé sur cette question et j'ai remarqué qu'AFAIU, aucune réponse ne correspondait parfaitement à mes besoins. J'ai donc développé jISO8601 et l' ai poussé sur maven central.

Ajoutez simplement en 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");

J'espère que cela vous aidera.

gturri
la source
0

Pour simplement formater une date comme celle-ci, les éléments suivants ont fonctionné pour moi dans une application basée sur Java 6. Il y a une DateFormatclasse JacksonThymeleafISO8601DateFormatdans 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.

ekip
la source
-1

Fonction de base Courtoisie: @wrygiel.

Cette fonction peut convertir le format ISO8601 en date Java qui peut gérer les valeurs de décalage. Selon la définition de l'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

  • Chaîne ISO8601 à l'objet Date (Local TimeZone)
  • Date en chaîne ISO8601
  • L'heure d'été est calculée automatiquement

Exemples de chaînes 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;
}

}

AKh
la source
2
Attention - DateFormat et les classes dérivées ne sont pas compatibles multithread! L'utilisation d'objets statiques SimpleDateFormat tels que DATE_FORMAT_1 et DATE_FORMAT_2 signifie que plusieurs threads appelant les fonctions ISO8601DateFormatter partageront le même objet DateFormat. Cela entraîne une corruption des données et des dates incorrectes renvoyées par les appels DateFormat. Pour résoudre ce problème, vous devez simplement créer les constantes de chaînes de modèle et créer des variables SimpleDateFormat locales chaque fois que nécessaire. Cela garantira que chaque objet est juste utilisé par un thread.
Theo
Une meilleure solution pour la sécurité des threads est d'utiliser à la place une bibliothèque date-heure conçue pour la sécurité des threads. En Java, ce monde peut être Joda-Time ou java.time.
Basil Bourque
-1

Cela a semblé 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 des chaînes de date JavaScript en / en Java. J'ai trouvé les travaux ci-dessus avec la recommandation. Il y avait quelques exemples d'utilisation de SimpleDateFormat qui étaient proches mais ils ne semblaient pas être le sous-ensemble recommandé par:

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

et pris en charge par PLIST et JavaScript Strings et ce qui est ce dont j'avais besoin.

Cela semble être la forme la plus courante de chaîne ISO8601 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 évalué, mais je suppose que ce sera assez rapide. Cela 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);
}
RickHigh
la source
-2

Je pense que ce que beaucoup de gens veulent faire, c'est analyser les chaînes de date JSON. Si vous arrivez sur cette page, il y a de fortes chances que vous souhaitiez convertir une date JSON JavaScript en 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.412Z.

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 que très important.

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

En l'occurrence, j'ai écrit un analyseur JSON et un analyseur PLIST qui utilisent tous les 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 de l'ISO 8601 ....

(J'ai posté l'autre dans l'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 millisecondes donc ... toutes les dates ISO-8601 ne sont pas identiques. (Au moins, je n'en ai pas encore trouvé un qui utilise des milis et l'analyseur que j'ai vu ignorer complètement le fuseau horaire OMG).

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

public static Date fromJsonDate( String string ) {

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

}

Notez que Reflection.toCharArray utilise dangereux s'il est disponible mais par défaut à string.toCharArray sinon.

(Vous pouvez le retirer de l'exemple en remplaçant Reflection.toCharArray (string) par string.toCharArray ()).

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

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

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

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

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

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


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

    }   else {
        return null;
    }

}

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

Quoi qu'il en soit ... je suppose que bon nombre de personnes qui viennent ici .. pourraient rechercher la chaîne de date JSON et bien qu'il s'agisse d'une date ISO-8601, elle est très spécifique et 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é indépendamment par les gars de Gatling Performance.

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 possède l'analyseur JSON le plus rapide pour les flux, lecteurs, octets [], char [], CharSequence (StringBuilder, CharacterBuffer) et String.

Voir plus de repères sur:

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

RickHigh
la source
Cette réponse sur JSON est hors sujet de la question. En outre, cette question est incorrecte car il n'existe pas de «date JSON» parmi les très rares types de données JSON . Et de nos jours, tout ce code peut être remplacé par un appel sur une seule ligne à la fonction Java intégrée:Instant.parse( "2013-12-14T01:55:33.412Z" )
Basil Bourque