Unzulässiges Musterzeichen 'T' beim Parsen einer Datumszeichenfolge in java.util.Date

181

Ich habe eine Datumszeichenfolge und möchte sie mithilfe der Java-Datums-API auf das normale Datum analysieren. Folgendes ist mein 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();
    }
}

Ich habe jedoch eine Ausnahme: java.lang.IllegalArgumentException: Illegal pattern character 'T'

Ich frage mich also, ob ich den String teilen und manuell analysieren muss.

Übrigens habe ich versucht, auf beiden Seiten des T ein einfaches Anführungszeichen einzufügen:

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

Es funktioniert auch nicht.

hguser
quelle
1
Weißt du was dieses "T" bedeutet? Soweit ich denke, sollte das gesamte Datum zusammen mit dem 'T' in das Datum konvertiert werden (nicht durch 'Einzelzitate' überspringen, aber das, was uns fehlt, ist das
Analysemuster
9
Das Tsteht für Zeit und ist Teil des ISO8601- Standards für die internationale Formatierung von Datum und Uhrzeit.
1
Das ist seltsam. Das Einschließen der Teinfachen Anführungszeichen hat bei mir funktioniert (zumindest im Hinblick auf die Lösung des Analysefehlers).
Agi Hammerthief

Antworten:

203

Update für Java 8 und höher

Sie können jetzt einfach Instant.parse("2015-04-28T14:23:38.521Z")das Richtige tun und erhalten, zumal Sie Instantanstelle der java.util.Datemit den neuesten Versionen von Java defekten verwenden sollten .

Sie sollten DateTimeFormatterstattdessen SimpleDateFormatterauch verwenden.

Ursprüngliche Antwort:

Die folgende Erklärung gilt weiterhin als das, was das Format darstellt. Es wurde jedoch geschrieben, bevor Java 8 allgegenwärtig war, sodass die alten Klassen verwendet werden, die Sie nicht verwenden sollten, wenn Sie Java 8 oder höher verwenden.

Dies funktioniert mit der Eingabe mit dem Trailing Zwie gezeigt:

In dem Muster wird das auf beiden Seiten Tmit 'Escapezeichen versehen.

Das Muster für das Zam Ende ist tatsächlich so, XXXwie es im JavaDoc für dokumentiert SimpleDateFormatist. Es ist nur nicht sehr klar, wie es tatsächlich verwendet werden soll, da Zes auch der Marker für die alten TimeZoneInformationen ist.

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

Erzeugt die folgende Ausgabe:

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

quelle
26

tl; dr

Verwenden Sie java.time.Instantclass, um Text im Standardformat ISO 8601 zu analysieren, der einen Moment in UTC darstellt.

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

ISO 8601

Dieses Format ist in der Norm ISO 8601 für Datums- und Uhrzeitzeichenfolgenformate definiert.

Beide:

… Verwenden standardmäßig ISO 8601-Formate zum Parsen und Generieren von Zeichenfolgen.

Sie sollten generell vermeiden, die alten Klassen java.util.Date /.Calendar & java.text.SimpleDateFormat zu verwenden, da diese notorisch problematisch, verwirrend und fehlerhaft sind. Bei Bedarf für die Zusammenarbeit können Sie hin und her konvertieren.

java.time

In Java 8 und höher ist das neue java.time- Framework integriert. Inspiriert von Joda-Time , definiert durch JSR 310 und erweitert durch das ThreeTen-Extra- Projekt.

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

In die alte Klasse konvertieren.

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

Zeitzone

Bei Bedarf können Sie eine Zeitzone zuweisen.

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.

Konvertieren.

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

Joda-Zeit

UPDATE: Das Joda-Time-Projekt befindet sich jetzt im Wartungsmodus. Das Team empfiehlt die Migration zu den Klassen java.time .

Hier ist ein Beispielcode 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.

In alte Klasse konvertieren. Beachten Sie, dass die zugewiesene Zeitzone bei der Konvertierung verloren geht, da juDate keine Zeitzone zugewiesen werden kann.

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

Zeitzone

Bei Bedarf können Sie eine Zeitzone zuweisen.

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

Tabelle der Datums- und Uhrzeittypen in Java, sowohl modern als auch alt.


Über java.time

Das java.time- Framework ist in Java 8 und höher integriert. Diese Klassen verdrängen die lästigen alten Legacy - Datum-Zeit - Klassen wie java.util.Date, Calendar, & SimpleDateFormat.

Das Joda-Time- Projekt, das sich jetzt im Wartungsmodus befindet , empfiehlt die Migration zu den Klassen java.time .

Weitere Informationen finden Sie im Oracle-Lernprogramm . Suchen Sie im Stapelüberlauf nach vielen Beispielen und Erklärungen. Die Spezifikation ist JSR 310 .

Sie können java.time- Objekte direkt mit Ihrer Datenbank austauschen . Verwenden Sie einen JDBC-Treiber, der mit JDBC 4.2 oder höher kompatibel ist . Keine Notwendigkeit für Zeichenfolgen, keine Notwendigkeit für java.sql.*Klassen.

Woher bekomme ich die java.time-Klassen?

Tabelle, welche java.time-Bibliothek mit welcher Java- oder Android-Version verwendet werden soll.

Das ThreeTen-Extra- Projekt erweitert java.time um zusätzliche Klassen. Dieses Projekt ist ein Testfeld für mögliche zukünftige Ergänzungen von java.time. Sie können einige nützliche Klassen hier wie finden Interval, YearWeek, YearQuarter, und mehr .

Basil Bourque
quelle
1
@AalapPatel Beachten Sie, dass sich das Joda-Time-Projekt jetzt im Wartungsmodus befindet. Das Team empfiehlt die Migration zu den Klassen java.time. Beide Bemühungen werden von demselben Mann, Stephen Colebourne, geleitet.
Basil Bourque
Vielen Dank, ich verwende es, um Millisekunden von der vom Server zurückgegebenen UTC-Zeit und / oder Gebietsschemazeit zu erhalten, und funktioniert in diesem Fall gut für mich.
Aalap Patel
Instant.parse braucht min api Level 26 in Android
Naveed Ahmad
1
@NaveedAhmad Siehe die Aufzählungszeichen unten in der Antwort zu ThreeTen-Backport- und ThreeTenABP- Projekten.
Basil Bourque
0

Es gibt zwei Antworten über up-to-jetzt und sie sind sowohl lang (und tl; dr zu kurz IMHO), so dass ich Zusammenfassung aus meiner Erfahrung schreiben beginne neu zu verwenden java.time Bibliothek (anwendbar wie in anderen Antworten auf Java - Version festgestellt 8+). ISO 8601 legt die Standardmethode zum Schreiben von Datumsangaben fest. YYYY-MM-DDDaher ist das Format der Datums- und Uhrzeitangabe nur wie folgt (kann 0, 3, 6 oder 9 Stellen für Millisekunden sein) und es ist keine Formatierungszeichenfolge erforderlich:

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

Ich brauchte es nicht, aber da das Abrufen des Jahres im Code der Frage enthalten ist:
Es ist schwieriger, kann nicht Instantdirekt ausgeführt werden, kann über CalendarFragen erfolgen. Holen Sie sich den ganzzahligen Wert des aktuellen Jahres in Java und konvertieren Sie Java. Zeit bis zum Kalender, aber IMHO als Format ist fester Teilstring ist einfacher zu verwenden:

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