Carattere di modello illegale 'T' durante l'analisi di una stringa di data in java.util.Date


182

Ho una stringa di data e voglio analizzarla alla data normale utilizzando l'API Date Java, il seguente è il mio codice:

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

Tuttavia ho ottenuto un'eccezione: java.lang.IllegalArgumentException: Illegal pattern character 'T'

Quindi mi chiedo se devo dividere la stringa e analizzarla manualmente?

A proposito, ho provato ad aggiungere un singolo carattere di virgoletta su entrambi i lati della T:

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

Inoltre non funziona.


1
sai cosa significa questa "T"? Per quanto penso che l'intera data insieme alla "T" dovrebbe essere convertita nella data (senza saltarla attraverso "Single Quotes", ma la cosa che ci manca è il modello di analisi.
coding_idiot

9
L' Tacronimo di tempo e fa parte della ISO8601 standard per tempo la data internazionale formattazione.

1
È strano. Avvolgere le Tvirgolette singole ha funzionato per me (almeno in termini di risoluzione dell'errore di analisi).
Agi Hammerthief,

Risposte:


204

Aggiornamento per Java 8 e versioni successive

Ora puoi semplicemente fare Instant.parse("2015-04-28T14:23:38.521Z")e ottenere la cosa giusta ora, soprattutto perché dovresti usare Instantinvece di quello rotto java.util.Datecon le versioni più recenti di Java.

Dovresti usare anche al DateTimeFormatterposto di SimpleDateFormatter.

Risposta originale:

La spiegazione che segue è ancora valida come ciò che rappresenta il formato. Ma è stato scritto prima che Java 8 fosse onnipresente, quindi utilizza le vecchie classi che non dovresti usare se stai usando Java 8 o versioni successive.

Funziona con l'input con il trailing Zcome dimostrato:

Nel modello Tè evaso con 'su entrambi i lati.

Il modello per Zalla fine è in realtà XXXdocumentato in JavaDoc per SimpleDateFormat, non è molto chiaro su come usarlo dato che Zè anche il marcatore per le vecchie TimeZoneinformazioni.

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

Produce il seguente output:

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

26

tl; dr

Usa la java.time.Instantclasse per analizzare il testo in formato ISO 8601 standard, che rappresenta un momento in UTC.

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

ISO 8601

Tale formato è definito dallo standard ISO 8601 per i formati di stringa data-ora.

Tutti e due:

... usa i formati ISO 8601 per impostazione predefinita per l'analisi e la generazione di stringhe.

Dovresti generalmente evitare di usare le vecchie classi java.util.Date /.Calendar & java.text.SimpleDateFormat poiché sono notoriamente problematiche, confuse e imperfette. Se necessario per l'interoperabilità, è possibile convertire avanti e indietro.

java.time

Integrato in Java 8 e versioni successive è il nuovo framework java.time . Ispirato da Joda-Time , definito da JSR 310 , e ampliato dal progetto ThreeTen-Extra .

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

Converti nella vecchia classe.

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

Fuso orario

Se necessario, è possibile assegnare un fuso orario.

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.

Convertire.

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

Joda-Time

AGGIORNAMENTO: il progetto Joda-Time è ora in modalità manutenzione. Il team consiglia la migrazione alle classi java.time .

Ecco alcuni esempi di codice in 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.

Converti in vecchia classe. Si noti che il fuso orario assegnato viene perso durante la conversione, poiché juDate non può essere assegnato a un fuso orario.

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

Fuso orario

Se necessario, è possibile assegnare un fuso orario.

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

Tabella dei tipi di data e ora in Java, sia moderni che legacy.


Informazioni su java.time

Il framework java.time è integrato in Java 8 e versioni successive. Queste classi soppiantare la vecchia fastidiosi legacy classi data-time come java.util.Date, Calendar, e SimpleDateFormat.

Il progetto Joda-Time , ora in modalità manutenzione , consiglia la migrazione alle classi java.time .

Per saperne di più, consulta il tutorial Oracle . E cerca Stack Overflow per molti esempi e spiegazioni. La specifica è JSR 310 .

Puoi scambiare oggetti java.time direttamente con il tuo database. Utilizzare un driver JDBC conforme a JDBC 4.2 o successivo. Non c'è bisogno di stringhe, non c'è bisogno di java.sql.*classi.

Dove ottenere le classi java.time?

  • Java SE 8 , Java SE 9 e versioni successive
    • Built-in.
    • Parte dell'API Java standard con un'implementazione in bundle.
    • Java 9 aggiunge alcune funzionalità e correzioni minori.
  • Java SE 6 e Java SE 7
    • Gran parte della funzionalità java.time è trasferita su Java 6 e 7 in ThreeTen-Backport .
  • androide
    • Versioni successive di implementazioni di bundle Android delle classi java.time.
    • Per precedenza Android (<26), il ThreeTenABP progetto si adatta ThreeTen-Backport (di cui sopra). Vedi Come usare ThreeTenABP… .

Tabella di quale libreria java.time utilizzare con quale versione di Java o Android.

Il progetto ThreeTen-Extra estende java.time con classi aggiuntive. Questo progetto è un banco di prova per possibili aggiunte future a java.time. Si possono trovare alcune classi utili, come per esempio Interval, YearWeek, YearQuartere altro .


1
@AalapPatel Tenere presente che il progetto Joda-Time è ora in modalità manutenzione. Il team consiglia la migrazione alle classi java.time. Entrambi gli sforzi sono guidati dallo stesso uomo, Stephen Colebourne.
Basil Bourque,

Grazie, lo uso per ottenere millisecondi dal server UTC restituito ora e / o ora locale e funziona bene per me in questo caso ..
Aalap Patel

Instant.parse ha bisogno di un livello minimo di API 26 in Android
Naveed Ahmad,

1
@NaveedAhmad Vedi i punti elenco in fondo alla risposta sui progetti ThreeTen-Backport e ThreeTenABP .
Basil Bourque,

0

Ci sono due risposte sopra fino ad ora e sono entrambe lunghe (e tl; dr troppo breve IMHO), quindi scrivo un riassunto della mia esperienza iniziando a usare la nuova libreria java.time (applicabile come notato in altre risposte alla versione Java 8+). ISO 8601 imposta il modo standard per scrivere le date: YYYY-MM-DDquindi il formato di data-ora è solo come sotto (potrebbe essere 0, 3, 6 o 9 cifre per millisecondi) e non è necessaria alcuna stringa di formattazione:

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

Non ne avevo bisogno, ma poiché ottenere l'anno è in codice dalla domanda, quindi:
è più complicato, non può essere fatto Instantdirettamente, può essere fatto tramite Calendardomande Ottieni valore intero dell'anno corrente in Java e Conversione Java. il tempo di Calendar ma IMHO come formato è fisso, la sottostringa è più semplice da usare:

myDate.toString().substring(0,4);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.