Caractère de modèle non autorisé 'T' lors de l'analyse d'une chaîne de date en java.util.Date

190

J'ai une chaîne de date et je veux l'analyser à la date normale en utilisant l'API java Date, ce qui suit est mon code:

public static void main(String[] args) {
    String date="2010-10-02T12:23:23Z";
    String pattern="yyyy-MM-ddThh:mm:ssZ";
    SimpleDateFormat sdf=new SimpleDateFormat(pattern);
    try {
        Date d=sdf.parse(date);
        System.out.println(d.getYear());
    } catch (ParseException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

Cependant j'ai eu une exception: java.lang.IllegalArgumentException: Illegal pattern character 'T'

Je me demande donc si je dois diviser la chaîne et l'analyser manuellement?

BTW, j'ai essayé d'ajouter un caractère guillemet simple de chaque côté du T:

String pattern="yyyy-MM-dd'T'hh:mm:ssZ";

Cela ne fonctionne pas non plus.

hguser
la source
1
savez-vous ce que signifie ce «T»? Pour autant que je pense, la date entière avec le `` T '' devrait être convertie en date (sans la sauter par `` Single Quotes '', mais ce qui nous manque, c'est le modèle d'analyse.
coding_idiot
10
Le Treprésente l' heure et fait partie de la norme ISO8601 pour le formatage international de la date et de l'heure.
1
C'est étrange. Emballer les Tguillemets simples a fonctionné pour moi (au moins en termes de résolution de l'erreur d'analyse).
Agi Hammerthief

Réponses:

210

Mise à jour pour Java 8 et supérieur

Vous pouvez maintenant simplement faire Instant.parse("2015-04-28T14:23:38.521Z")et obtenir la bonne chose maintenant, d'autant plus que vous devriez utiliser à la Instantplace de la version cassée java.util.Dateavec les versions les plus récentes de Java.

Vous devriez également utiliser DateTimeFormatterau lieu de SimpleDateFormatter.

Réponse originale:

L'explication ci-dessous est toujours valable comme ce que le format représente. Mais il a été écrit avant que Java 8 ne soit omniprésent, il utilise donc les anciennes classes que vous ne devriez pas utiliser si vous utilisez Java 8 ou supérieur.

Cela fonctionne avec l'entrée avec la fin Zcomme démontré:

Dans le modèle, le Test échappé de 'chaque côté.

Le modèle pour le Zà la fin est en fait XXXtel que documenté dans le JavaDoc pour SimpleDateFormat, il n'est tout simplement pas très clair sur la manière de l'utiliser, car Zc'est également le marqueur des anciennes TimeZoneinformations.

Q2597083.java

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;

public class Q2597083
{
    /**
     * All Dates are normalized to UTC, it is up the client code to convert to the appropriate TimeZone.
     */
    public static final TimeZone UTC;

    /**
     * @see <a href="http://en.wikipedia.org/wiki/ISO_8601#Combined_date_and_time_representations">Combined Date and Time Representations</a>
     */
    public static final String ISO_8601_24H_FULL_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX";

    /**
     * 0001-01-01T00:00:00.000Z
     */
    public static final Date BEGINNING_OF_TIME;

    /**
     * 292278994-08-17T07:12:55.807Z
     */
    public static final Date END_OF_TIME;

    static
    {
        UTC = TimeZone.getTimeZone("UTC");
        TimeZone.setDefault(UTC);
        final Calendar c = new GregorianCalendar(UTC);
        c.set(1, 0, 1, 0, 0, 0);
        c.set(Calendar.MILLISECOND, 0);
        BEGINNING_OF_TIME = c.getTime();
        c.setTime(new Date(Long.MAX_VALUE));
        END_OF_TIME = c.getTime();
    }

    public static void main(String[] args) throws Exception
    {

        final SimpleDateFormat sdf = new SimpleDateFormat(ISO_8601_24H_FULL_FORMAT);
        sdf.setTimeZone(UTC);
        System.out.println("sdf.format(BEGINNING_OF_TIME) = " + sdf.format(BEGINNING_OF_TIME));
        System.out.println("sdf.format(END_OF_TIME) = " + sdf.format(END_OF_TIME));
        System.out.println("sdf.format(new Date()) = " + sdf.format(new Date()));
        System.out.println("sdf.parse(\"2015-04-28T14:23:38.521Z\") = " + sdf.parse("2015-04-28T14:23:38.521Z"));
        System.out.println("sdf.parse(\"0001-01-01T00:00:00.000Z\") = " + sdf.parse("0001-01-01T00:00:00.000Z"));
        System.out.println("sdf.parse(\"292278994-08-17T07:12:55.807Z\") = " + sdf.parse("292278994-08-17T07:12:55.807Z"));
    }
}

Produit la sortie suivante:

sdf.format(BEGINNING_OF_TIME) = 0001-01-01T00:00:00.000Z
sdf.format(END_OF_TIME) = 292278994-08-17T07:12:55.807Z
sdf.format(new Date()) = 2015-04-28T14:38:25.956Z
sdf.parse("2015-04-28T14:23:38.521Z") = Tue Apr 28 14:23:38 UTC 2015
sdf.parse("0001-01-01T00:00:00.000Z") = Sat Jan 01 00:00:00 UTC 1
sdf.parse("292278994-08-17T07:12:55.807Z") = Sun Aug 17 07:12:55 UTC 292278994

la source
27

tl; dr

Utilisez la java.time.Instantclasse pour analyser le texte au format standard ISO 8601, représentant un moment UTC.

Instant.parse( "2010-10-02T12:23:23Z" )

ISO 8601

Ce format est défini par la norme ISO 8601 pour les formats de chaîne date-heure.

Tous les deux:

… Utiliser les formats ISO 8601 par défaut pour analyser et générer des chaînes.

Vous devez généralement éviter d'utiliser les anciennes classes java.util.Date /.Calendar & java.text.SimpleDateFormat car elles sont notoirement gênantes, déroutantes et défectueuses. Si nécessaire pour l'interopérabilité, vous pouvez convertir en va-et-vient.

java.time

Le nouveau framework java.time est intégré à Java 8 et versions ultérieures . Inspiré de Joda-Time , défini par JSR 310 , et étendu par le projet ThreeTen-Extra .

Instant instant = Instant.parse( "2010-10-02T12:23:23Z" );  // `Instant` is always in UTC.

Convertissez-vous dans l'ancienne classe.

java.util.Date date = java.util.Date.from( instant );  // Pass an `Instant` to the `from` method.

Fuseau horaire

Si nécessaire, vous pouvez attribuer un fuseau horaire.

ZoneId zoneId = ZoneId.of( "America/Montreal" ); // Define a time zone rather than rely implicitly on JVM’s current default time zone.
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );  // Assign a time zone adjustment from UTC.

Convertir.

java.util.Date date = java.util.Date.from( zdt.toInstant() );  // Extract an `Instant` from the `ZonedDateTime` to pass to the `from` method.

Joda-Time

MISE À JOUR: Le projet Joda-Time est maintenant en mode maintenance. L'équipe conseille la migration vers les classes java.time .

Voici un exemple de code dans Joda-Time 2.8.

org.joda.time.DateTime dateTime_Utc = new DateTime( "2010-10-02T12:23:23Z" , DateTimeZone.UTC );  // Specifying a time zone to apply, rather than implicitly assigning the JVM’s current default.

Convertissez-vous en ancienne classe. Notez que le fuseau horaire attribué est perdu lors de la conversion, car juDate ne peut pas recevoir de fuseau horaire.

java.util.Date date = dateTime_Utc.toDate(); // The `toDate` method converts to old class.

Fuseau horaire

Si nécessaire, vous pouvez attribuer un fuseau horaire.

DateTimeZone zone = DateTimeZone.forID( "America/Montreal" );
DateTime dateTime_Montreal = dateTime_Utc.withZone ( zone );

Tableau des types date-heure en Java, à la fois modernes et hérités.


À 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 , maintenant 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.*classes.

Où obtenir les classes java.time?

  • Java SE 8 , Java SE 9 et versions ultérieures
    • Intégré.
    • Partie de l'API Java standard avec une implémentation groupée.
    • Java 9 ajoute quelques fonctionnalités et corrections mineures.
  • 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 bundle 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… .

Tableau de la bibliothèque java.time à utiliser avec quelle version de Java ou d'Android.

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

Basil Bourque
la source
1
@AalapPatel Sachez que le projet Joda-Time est désormais en mode maintenance. L'équipe conseille la migration vers les classes java.time. Les deux efforts sont dirigés par le même homme, Stephen Colebourne.
Basil Bourque
Merci, je l'utilise pour obtenir des millisecondes à partir de l'heure UTC renvoyée par le serveur et / ou de l'heure locale et fonctionne bien pour moi dans ce cas.
Aalap Patel
Instant.parse nécessite un niveau API minimum 26 dans Android
Naveed Ahmad
1
@NaveedAhmad Voir les puces au bas de la réponse sur les projets ThreeTen-Backport et ThreeTenABP .
Basil Bourque
1

Il y a deux réponses ci-dessus jusqu'à présent et elles sont toutes les deux longues (et tl; dr trop court à mon humble avis ), donc j'écris un résumé de mon expérience en commençant à utiliser la nouvelle bibliothèque java.time (applicable comme indiqué dans d'autres réponses à la version Java 8+). ISO 8601 définit la manière standard d'écrire les dates: YYYY-MM-DDle format date-heure est donc uniquement comme ci-dessous (peut être 0, 3, 6 ou 9 chiffres pour les millisecondes) et aucune chaîne de formatage n'est nécessaire:

import java.time.Instant;
public static void main(String[] args) {
    String date="2010-10-02T12:23:23Z";
    try {
        Instant myDate = Instant.parse(date);
    } catch (ParseException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

Je n'en avais pas besoin, mais comme obtenir l'année est dans le code à partir de la question, alors:
c'est plus délicat, ne peut pas être fait Instantdirectement, peut être fait via Calendardes questions Obtenir la valeur entière de l'année en cours en Java et convertir java. time to Calendar mais à mon humble avis, car le format est une sous-chaîne fixe est plus simple à utiliser:

myDate.toString().substring(0,4);
Alexei Martianov
la source