807 Stimmen

Konvertierung von ISO 8601-konformem String in java.util.Date

Ich versuche, eine ISO 8601 formatierte Zeichenkette in eine java.util.Date .

Ich habe das Muster gefunden yyyy-MM-dd'T'HH:mm:ssZ ISO8601-konform zu sein, wenn sie mit einem Gebietsschema verwendet wird (siehe Beispiel).

Allerdings ist die Verwendung der java.text.SimpleDateFormat kann ich den korrekt formatierten String nicht umwandeln 2010-01-01T12:00:00+01:00 . Ich muss es zunächst in 2010-01-01T12:00:00+0100 , ohne den Doppelpunkt.

Die derzeitige Lösung lautet also

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

was natürlich nicht so schön ist. Übersehe ich etwas oder gibt es eine bessere Lösung?


Antwort

Dank des Kommentars von JuanZe habe ich die Joda-Time Magie, sie ist auch hier beschrieben .

Die Lösung lautet also

DateTimeFormatter parser2 = ISODateTimeFormat.dateTimeNoMillis();
String jtdate = "2010-01-01T12:00:00+01:00";
System.out.println(parser2.parseDateTime(jtdate));

Oder einfacher: Verwenden Sie den Standard-Parser über den Konstruktor:

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

Für mich ist das schön.

283 Stimmen

Seien Sie darauf gefasst, dass Sie eine Menge "Use JodaTime"-Antworten erhalten werden...

3 Stimmen

@Ice09: Wenn die API-Dokumentation für DateTimeFormat korrekt ist (die JoDa-Dokumentation kann allerdings irreführend, falsch oder unvollständig sein), ist das Muster, das Sie in Ihrer eigenen "Antwort" verwendet haben, nicht mit ISO8601 kompatibel.

0 Stimmen

@jarnbjo: Danke für den Kommentar - du könntest Recht haben, ich habe noch einmal nachgeschaut und bin auf die viel bessere Lösung ISODateTimeFormat.dateTimeNoMillis() gekommen, die irgendwie garantiert, dass das richtige Format verwendet wird.

514voto

jarnbjo Punkte 33136

Leider sind die verfügbaren Zeitzonenformate für SimpleDateFormat (Java 6 und früher) sind nicht ISO 8601 nachgiebig. SimpleDateFormat versteht Zeitzonen-Strings wie "GMT+01:00" oder "+0100", letzteres gemäß RFC # 822 .

Auch wenn Java 7 die Unterstützung für Zeitzonen-Deskriptoren nach ISO 8601 hinzugefügt hat, ist SimpleDateFormat immer noch nicht in der Lage, einen vollständigen Datumsstring richtig zu analysieren, da es keine Unterstützung für optionale Teile hat.

Die Umformatierung der Eingabezeichenfolge mit regexp ist sicherlich eine Möglichkeit, aber die Ersetzungsregeln sind nicht so einfach wie in Ihrer Frage:

  • Einige Zeitzonen sind nicht ganz stundenfrei UTC Die Zeichenfolge endet also nicht unbedingt mit ":00".
  • ISO8601 lässt nur die Anzahl der Stunden in der Zeitzone zu, so dass "+01" gleichbedeutend mit "+01:00" ist.
  • ISO8601 erlaubt die Verwendung von "Z" zur Angabe von UTC anstelle von "+00:00".

Die einfachere Lösung ist möglicherweise die Verwendung des Datentypkonverters in JAXB, da JAXB in der Lage sein muss, ISO8601 Datumsstrings gemäß der XML-Schema-Spezifikation zu parsen. javax.xml.bind.DatatypeConverter.parseDateTime("2010-01-01T12:00:00Z") erhalten Sie eine Calendar Objekt und Sie können einfach getTime() darauf anwenden, wenn Sie eine Date Objekt.

Sie können wahrscheinlich Joda-Time auch, aber ich weiß nicht, warum man sich damit beschäftigen sollte (Update 2022; vielleicht weil die gesamte javax.xml.bind Abschnitt fehlt in Androids javax.xml Paket).

19 Stimmen

Die JAXB-Lösung ist ein wirklich kreativer Ansatz! Es funktioniert auch, ich habe es mit meinem Beispiel getestet. Allerdings würde ich jedem, der das Problem hat und JodaTime verwenden darf, dazu raten, da es sich natürlicher anfühlt. Aber Ihre Lösung erfordert keine zusätzlichen Bibliotheken (zumindest mit Java 6).

38 Stimmen

Hier ist die Umkehrung: Calendar c = GregorianCalendar.getInstance();c.setTime(aDate);return javax.xml.bind.DatatypeConverter.printDateTime(c);

0 Stimmen

Wow, das ist ein wirklich nicht offensichtlicher Ort, um so etwas Nützliches unterzubringen. Ich suche schon seit ein paar Tagen.

322voto

Antonio Punkte 11529

Die Art und Weise, wie das ist gesegnet von der Java 7 Dokumentation :

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

Weitere Beispiele finden Sie im Abschnitt Beispiele unter SimpleDateFormat javadoc .

UPDATE 13.02.2020: Es gibt eine völlig neue Art um dies in Java 8 zu tun

7 Stimmen

Ihre Antwort hat mir geholfen, das ISODate von MongoDB in ein lokales Datum zu konvertieren. Mit freundlichen Grüßen.

1 Stimmen

Dies gilt ab Java 7

10 Stimmen

@b.long Java hat mehr als eine Konstante für solche ISO 8601-konformen Formate hinzugefügt. Java hat ein ganz neues Framework für die Arbeit mit Datumsangaben erhalten, das eine eingebaute Standardunterstützung für solche Formate enthält. Siehe die neue java.time Rahmenwerk in Java 8, inspiriert von Joda-Time und ersetzt damit die lästigen Klassen java.util.Date, .Calendar und SimpleDateFormat.

210voto

wrygiel Punkte 4945

Okay, diese Frage ist bereits beantwortet, aber ich gebe meine Antwort trotzdem. Sie könnte jemandem helfen.

Ich habe nach einem Lösung für Android (API 7).

  • Joda kam nicht in Frage - es ist riesig und leidet unter einer langsamen Initialisierung. Es schien auch ein großer Overkill für diesen speziellen Zweck.
  • Antworten mit javax.xml funktioniert nicht mit Android API 7.

Ich habe schließlich diese einfache Klasse implementiert. Sie deckt nur die häufigste Form von ISO 8601-Zeichenfolgen, aber das sollte in manchen Fällen ausreichen (wenn Sie sicher sind, dass die Eingabe in 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;
    }
}

Anmerkung zur Leistung: Ich instanziere jedes Mal ein neues SimpleDateFormat, um zu vermeiden eine Wanze in Android 2.1. Wenn Sie genauso erstaunt sind wie ich, lesen Sie dieses Rätsel . Bei anderen Java-Engines können Sie die Instanz in einem privaten statischen Feld zwischenspeichern (mit ThreadLocal, um thread-sicher zu sein).

3 Stimmen

Vielleicht hätte man daraus eine eigene Frage mit einer eigenen Antwort machen sollen?

5 Stimmen

Dies war die erste Seite, über die ich gestolpert bin, als ich nach der Antwort suchte, also schien es zu passen. Für die meisten Java-Entwickler ist Android nicht genau Java. In den meisten Fällen funktioniert jedoch das eine wie das andere, so dass viele Android-Entwickler nach "Java" suchen, wenn sie nach dieser Frage suchen.

0 Stimmen

154voto

Adam Punkte 34323

Java.time

El java.time API (in Java 8 und höher integriert), macht dies ein wenig einfacher.

Wenn Sie wissen, dass die Eingabe in UTC wie zum Beispiel die Z (für Zulu) am Ende, die Instant Klasse parsen kann.

java.util.Date date = Date.from( Instant.parse( "2014-12-12T10:39:40Z" ));

Wenn Ihr Beitrag ein anderer sein kann Abweichung von der UTC Werte und nicht UTC angezeigt durch die Z (Zulu) am Ende, verwenden Sie die OffsetDateTime Klasse zu analysieren.

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

Dann extrahieren Sie eine Instant und konvertieren in eine java.util.Date durch den Aufruf from .

Instant instant = odt.toInstant();  // Instant is always in UTC.
java.util.Date date = java.util.Date.from( instant );

15 Stimmen

Diese Antwort ist zu anstrengend. Ein java.util.Date hat per Definition keine Zeitzone. Es besteht also keine Notwendigkeit für den ganzen zeitzonenbezogenen Code: die LocalDateTimeZoneIdatZone . Dieser einfache Einzeiler ist ausreichend: java.util.Date date = Date.from( ZonedDateTime.parse( "2014-12-12T10:39:40Z" ).toInstant() );

0 Stimmen

@BasilBourque Danke, ich wusste, dass es falsch war, über LocalDateTime zu gehen, aber ich konnte keinen schnellen Weg finden, die Antwort wurde aktualisiert.

7 Stimmen

@BasilBourque Das ist unnötig kompliziert: Date.from(Instant.parse("2014-12-12T10:39:40Z" )); ist ausreichend.

82voto

Antonio Punkte 11529

Ab Java 8 gibt es eine völlig neue, offiziell unterstützte Möglichkeit, dies zu tun:

    String s = "2020-02-13T18:51:09.840Z";
    TemporalAccessor ta = DateTimeFormatter.ISO_INSTANT.parse(s);
    Instant i = Instant.from(ta);
    Date d = Date.from(i);

3 Stimmen

Wenn die Zeichenkette im Instant-Format vorliegt, mit nachgestellter Z als Offset, müssen wir dies nicht explizit angeben. Einfach Instant i = Instant.parse(s); . Die Zeichenkette in der Frage hatte +01:00 in diesem Fall DateTimeFormatter.ISO_INSTANT funktioniert nicht (zumindest nicht auf meinem Java 11).

5 Stimmen

@OleV.V. Sie können verwenden ISO_OFFSET_DATE_TIME zur Formatierung von Daten mit Offsets, wie +01:00 ( docs.oracle.com/javase/8/docs/api/java/time/format/ )

1 Stimmen

Das ist wahr, @LucasBasquerotto. Auch wenn dieser Formatierer nicht explizit erwähnt wird, sind die Antworten von Adamvon Basil Bourque machen bereits etwas Ähnliches.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X