773 votes

Conversion d'un String conforme à la norme ISO 8601 en java.util.Date

J'essaie de convertir un ISO 8601 vers une chaîne de caractères formatée java.util.Date .

J'ai trouvé le modèle yyyy-MM-dd'T'HH:mm:ssZ pour être conforme à la norme ISO8601 s'il est utilisé avec une Locale (comparer l'exemple).

Cependant, l'utilisation du 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.

La solution actuelle est donc

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 rate quelque chose ou existe-t-il une meilleure solution ?


Réponse :

Grâce au commentaire de JuanZe, j'ai trouvé l'adresse suivante Joda-Time magique, c'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 le parseur par défaut via le constructeur :

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

Pour moi, c'est bien.

276 votes

Préparez-vous à recevoir beaucoup de réponses "Utilisez JodaTime"...

0 votes

Oh, j'adore JodaTime - mais je ne savais pas qu'il pouvait m'aider ici.....

0 votes

Avez-vous essayé différentes langues ? Je me demande s'il ne s'agit pas d'un bogue spécifique à la locale en ce qui concerne l'analyse des fuseaux horaires.

508voto

jarnbjo Points 18238

Malheureusement, les formats de fuseau horaire disponibles pour SimpleDateFormat (Java 6 et antérieurs) ne sont pas ISO 8601 conforme. SimpleDateFormat comprend les chaînes de fuseau horaire telles que "GMT+01:00" ou "+0100", cette dernière étant conforme à la norme ISO 9001:2000. RFC # 822 .

Même si Java 7 a ajouté la prise en charge des descripteurs de fuseau horaire conformément à la norme 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.

Reformater votre chaîne d'entrée à l'aide d'une 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 La chaîne ne se termine donc pas nécessairement par ":00".
  • La norme ISO8601 ne permet d'inclure que le nombre d'heures dans le fuseau horaire, ainsi "+01" est équivalent à "+01:00".
  • La norme ISO8601 autorise l'utilisation de "Z" pour indiquer UTC au lieu de "+00:00".

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

Vous pourriez probablement utiliser Joda-Time également, mais je ne sais pas pourquoi vous devriez vous embêter avec ça.

19 votes

La solution JAXB est une approche vraiment créative ! Elle fonctionne aussi bien, je l'ai testée avec mon échantillon. Cependant, pour quiconque est confronté à ce problème et est autorisé à utiliser JodaTime, je conseillerais de l'utiliser, car cela semble plus naturel. Mais votre solution ne nécessite pas de bibliothèques supplémentaires (du moins avec Java 6).

38 votes

Voici l'inverse : Calendrier c = GregorianCalendar.getInstance();c.setTime(aDate);return javax.xml.bind.DatatypeConverter.printDateTime(c) ;

0 votes

Wow, c'est un endroit vraiment pas évident pour mettre une chose aussi utile. J'ai cherché pendant quelques jours.

302voto

Antonio Points 2291

La façon dont c'est béni par la documentation de 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 trouverez d'autres exemples dans la section Exemples à l'adresse SimpleDateFormat javadoc .

MISE À JOUR 13/02/2020 : Il existe un une toute nouvelle façon de faire pour faire cela en Java 8

7 votes

Votre réponse m'a aidé à convertir la date ISODate de MongoDB en date locale. Salutations.

1 votes

Ceci est valable à partir de java 7

10 votes

@b.long Java a ajouté plus qu'une constante pour ces formats conformes à la norme ISO 8601. Java s'est doté d'un nouveau cadre complet pour le travail sur les dates et les heures, qui inclut une prise en charge intégrée par défaut de ces formats. Voir le nouveau java.time cadre dans Java 8, inspiré par Joda-Time qui supplante les classes java.util.Date, .Calendar et SimpleDateFormat.

208voto

wrygiel Points 1857

Bon, on a déjà répondu à cette question, mais je vais quand même donner ma réponse. Cela pourrait aider quelqu'un.

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

  • Joda était hors de question - il est énorme et souffre d'une initialisation lente. De plus, il semblait être surdimensionné pour cet usage particulier.
  • Réponses impliquant javax.xml ne fonctionnera pas sur Android API 7.

J'ai fini par implémenter cette simple classe. Elle couvre seulement la forme la plus courante de chaînes ISO 8601, mais cela devrait suffire dans certains cas (lorsque vous êtes tout à fait sûr que l'entrée sera en format este 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 de performance : J'instancie un nouveau SimpleDateFormat à chaque fois afin d'éviter un bug dans Android 2.1. Si vous êtes aussi étonné que je l'étais, voir cette énigme . Pour d'autres moteurs Java, vous pouvez mettre en cache l'instance dans un champ statique privé (en utilisant ThreadLocal, pour être à l'abri des fils).

3 votes

Peut-être aurait-il fallu en faire une question à part entière, avec sa propre réponse ?

5 votes

C'est la première page sur laquelle j'ai buté en cherchant la réponse, donc ça me semblait approprié. Pour la plupart des développeurs Java, Android n'est pas exactement Java. Cependant, dans la plupart des cas, l'un fonctionne comme l'autre, donc de nombreux développeurs Android chercheront "java" lorsqu'ils chercheront ceci.

1 votes

Notez que cela ne tient pas compte de la résolution en millisecondes. Il est facile de l'ajouter.

72voto

david_p Points 483

Le site Bibliothèque Jackson-databind a également Classe ISO8601DateFormat qui fait cela (implémentation réelle dans ISO8601Utilitaires .

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

0 votes

Il ne parvient pas à analyser cette date : 2015-08-11T13:10:00 . Je reçois String index out of range: 19 . En regardant le code, il semble qu'il faille spécifier les millisecondes et le fuseau horaire. Ces éléments devraient être facultatifs.

3 votes

Pour citer la documentation, le format d'analyse est le suivant : [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.

2 votes

Ah oui, en fait, il semble que vous ayez raison. Cependant, je suis presque sûr que la norme ISO8601 permet d'omettre le fuseau horaire, donc c'est toujours faux. JodaTime fonctionne cependant : new DateTime("2015-08-11T13:10:00").toDate()

35voto

d.danailov Points 943

Pour Java version 7

Vous pouvez suivre la documentation d'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);

0 votes

Cela signifie que le fuseau horaire est requis . Selon la norme ISO 8601, elle est facultative. Tout comme les secondes, etc. Donc, cela n'analyse qu'un sous-ensemble spécifique de la norme ISO 8601.

3 votes

Fonctionne très bien avec Java 1.8

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X