Question Conversion de la chaîne compatible ISO 8601 en java.util.Date


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

J'ai trouvé le motif yyyy-MM-dd'T'HH:mm:ssZ être conforme à ISO8601 s'il est utilisé avec un environnement local (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 le convertir d'abord 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 bien. Ai-je raté quelque chose ou y a-t-il une meilleure solution?


Répondre

Grâce au commentaire de JuanZe, j'ai trouvé Joda-Time la magie, 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 l'analyseur par défaut via le constructeur:

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

Pour moi, c'est bien.


548
2018-02-04 17:52


origine


Réponses:


Malheureusement, les formats de fuseaux horaires disponibles pour SimpleDateFormat (Java 6 et plus tôt) ne sont pas ISO 8601 conforme. SimpleDateFormat comprend les chaînes de fuseaux horaires 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 capable d'analyser correctement une chaîne de date complète, car elle ne prend pas en charge les pièces facultatives.

Reformater votre chaîne d'entrée en 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 autorise uniquement le nombre d'heures à inclure dans le fuseau horaire, ainsi "+01" équivaut à "+01: 00"
  • ISO8601 permet l'utilisation de "Z" pour indiquer UTC au lieu de "+00: 00".

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

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


423
2018-02-04 18:51



D'accord, cette question est déjà répondu, mais je vais laisser tomber ma réponse de toute façon. 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 lente initialisation. Cela semblait également être une surpuissance majeure dans ce but particulier.
  • Réponses impliquant javax.xml ne fonctionnera pas sur Android API 7.

Fini l'implémentation de cette classe simple. Cela couvre seulement la forme la plus commune des chaînes ISO 8601, mais cela devrait suffire dans certains cas (quand vous êtes sûr que l'entrée sera en 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 de performance: J'instancie chaque fois SimpleDateFormat comme moyen d'éviter un 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 sûr pour les threads).


188
2018-05-16 15:16



La façon dont c'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.


168
2017-08-13 19:07



java.time

le API java.time (construit en Java 8 et plus tard), rend cela un peu plus facile.

Si vous connaissez l'entrée est en 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 contribution peut être une autre décalage-de-UTC valeurs plutôt que UTC indiqué 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 Instantet 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 );

66
2017-12-15 07:52



le Bibliothèque Jackson-databind a aussi Classe ISO8601DateFormat cela fait cela (mise en œuvre effective dans ISO8601Utils.

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

60
2017-07-05 14:23



tl; dr

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

Utiliser java.time

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

le OffsetDateTime classe représente un moment sur la ligne de temps avec un décalage-de-UTC mais pas un fuseau horaire.

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

Appel toString gé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, extraire un Instant ou ajuster 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 désiré. UNE fuseau horaire est une histoire de décalage-de-UTC des valeurs pour une région, avec un ensemble de règles pour gérer les anomalies telles que l'heure d'été (DST). Donc, appliquez un fuseau horaire plutôt qu'un simple décalage autant que possible.

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

Sur java.time

le java.time framework est construit dans Java 8 et plus tard. Ces classes supplantent le vieux problème héritage classes de date-heure telles que java.util.Date, Calendar, & SimpleDateFormat.

le Joda-Time projet, maintenant dans Mode de Maintenance, conseille la migration vers java.time Des classes.

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

Vous pouvez échanger java.time objets directement avec votre base de données. Utiliser un Pilote JDBC conforme à JDBC 4.2 ou plus tard. Pas besoin de cordes, pas besoin de java.sql.* Des classes.

Où obtenir les classes java.time?

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



31
2017-12-14 01:48



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
2018-04-08 14:20



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
2018-04-13 17:27