Konvertieren eines ISO 8601-kompatiblen Strings in java.util.Date

667

Ich versuche, einen ISO 8601- formatierten String in einen zu konvertieren java.util.Date.

Ich habe festgestellt, dass das Muster yyyy-MM-dd'T'HH:mm:ssZISO8601-kompatibel ist, wenn es mit einem Gebietsschema verwendet wird (vergleiche Beispiel).

Mit dem java.text.SimpleDateFormatkann ich den korrekt formatierten String jedoch nicht konvertieren 2010-01-01T12:00:00+01:00. Ich muss es zuerst konvertieren 2010-01-01T12:00:00+0100, ohne den Doppelpunkt.

Die aktuelle Lösung ist 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 offensichtlich nicht so schön ist. Vermisse ich etwas oder gibt es eine bessere Lösung?


Antworten

Dank JuanZes Kommentar habe ich die Joda-Zeit- Magie gefunden, die auch hier beschrieben wird .

Die Lösung ist also

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

Oder verwenden Sie einfach den Standardparser über den Konstruktor:

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

Für mich ist das schön.

Ice09
quelle
243
Seien Sie bereit, viele "Use JodaTime" -Antworten zu erhalten ...
JuanZe
3
@ Ice09: Wenn die API-Dokumentation für DateTimeFormat korrekt ist (die JoDa-Dokumentation kann jedoch irreführend, falsch oder unvollständig sein), ist das Muster, das Sie in Ihrer eigenen "Antwort" verwendet haben, nicht mit ISO8601 kompatibel.
jarnbjo
21
Ich bin nicht sicher, wann dies hinzugefügt wurde, aber das 'X' scheint dieses Problem in SimpleDateFormat zu lösen. Das Muster "JJJJ-MM-TT'T'HH: MM: SSX" analysiert das Beispiel in der Frage erfolgreich.
mlohbihler
12
Das 'X' ist seit Java 7 verfügbar.
Lars Grammel
3
Java 8 macht es einfach! Es gibt ein verstecktes Juwel von Adam in den Antworten unten: stackoverflow.com/a/27479533/1262901
Fabian Keller

Antworten:

477

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

Auch wenn Java 7 die Unterstützung für Zeitzonendeskriptoren gemäß ISO 8601 hinzugefügt hat, kann SimpleDateFormat eine vollständige Datumszeichenfolge nicht ordnungsgemäß analysieren, da optionale Teile nicht unterstützt werden.

Das Neuformatieren Ihrer Eingabezeichenfolge mit regulärem Ausdruck ist sicherlich eine Möglichkeit, aber die Ersetzungsregeln sind nicht so einfach wie in Ihrer Frage:

  • Einige Zeitzonen haben keine vollen Stunden vor UTC , daher endet die Zeichenfolge nicht unbedingt mit ": 00".
  • Nach ISO8601 kann nur die Anzahl der Stunden in die Zeitzone aufgenommen werden. "+01" entspricht also "+01: 00".
  • ISO8601 erlaubt die Verwendung von "Z", um UTC anstelle von "+00: 00" anzuzeigen.

Die einfachere Lösung besteht möglicherweise darin, den Datentypkonverter in JAXB zu verwenden, da JAXB in der Lage sein muss, die ISO8601-Datumszeichenfolge gemäß der XML-Schemaspezifikation zu analysieren. javax.xml.bind.DatatypeConverter.parseDateTime("2010-01-01T12:00:00Z")Sie erhalten ein CalendarObjekt und können einfach getTime () verwenden, wenn Sie ein DateObjekt benötigen .

Sie könnten wahrscheinlich auch Joda-Time verwenden , aber ich weiß nicht, warum Sie sich damit beschäftigen sollten.

jarnbjo
quelle
18
Die JAXB-Lösung ist ein wirklich kreativer Ansatz! Es funktioniert auch, ich habe es mit meiner Probe getestet. Für alle, die mit dem Problem konfrontiert sind und JodaTime verwenden dürfen, würde ich empfehlen, es zu verwenden, da es sich natürlicher anfühlt. Ihre Lösung erfordert jedoch keine zusätzlichen Bibliotheken (zumindest mit Java 6).
Ice09
36
Hier ist das Gegenteil: Kalender c = GregorianCalendar.getInstance (); c.setTime (aDate); return javax.xml.bind.DatatypeConverter.printDateTime (c);
Alexander Ljungberg
4
Eigentlich ist es nicht so einfach, dass Sie den jaxb-DatentypConverter initialisieren müssen. Am Ende habe ich DatatypeFactory selbst verwendet, wie es DataTypeConverterImpl intern getan hat. Was für Kopfschmerzen.
gtrak
3
@ Simon: Nein, Zeitzonen werden natürlich nicht ignoriert. Sie müssen etwas falsch machen. Wenn Sie mehr als ein paar Zeichen eingeben und uns sagen, was Sie wirklich tun, kann Ihnen jemand erklären, was.
jarnbjo
4
@jarnbjo du bist die erste und einzige Person, der ich begegnet bin, die die Standard-Java-Datumsklassen vor 1.8 gegenüber der Joda-Zeit bevorzugt. Ich finde Joda-Zeit eine buchstäbliche Freude, besonders im Vergleich zu der Standard-API, die ein Greuel ist.
NimChimpsky
244

Der Weg, der durch die Java 7-Dokumentation gesegnet ist :

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 .

UPD 13.02.2020: In Java 8 gibt es eine völlig neue Möglichkeit , dies zu tun

Antonio
quelle
7
Ihre Antwort hat mir geholfen, MongoDBs ISODate in ein lokales Datum umzuwandeln. Grüße.
Blue Sky
9
@ b.long Java hat mehr als eine Konstante für solche ISO 8601-kompatiblen Formate hinzugefügt. Java hat ein völlig neues Framework für die Arbeit mit Datum und Uhrzeit erhalten, das eine integrierte Standardunterstützung für solche Formate enthält. Sehen Sie sich das neue java.timeFramework in Java 8 an, das von Joda-Time inspiriert wurde und die problematischen Klassen java.util.Date, .Calendar und SimpleDateFormat ersetzt.
Basil Bourque
2
Bedeutet das nicht, dass Sie das Datumsformat im Voraus kennen müssen? Was ist, wenn Sie akzeptieren müssen string1und string2nicht wissen, welche Sie bekommen werden ?
Timmmm
16
'Z' muss in Anführungszeichen stehen
kervin
7
@kervin Wenn das Z in Anführungszeichen steht, sucht der Formatierer dann nicht speziell nach dem Zeichen Z, nicht nach allen versetzten Zeichenfolgen, die er darstellen kann? Es scheint, als würde das Zitieren von Z nur zufällig funktionieren, wenn sich Ihre Datumszeichenfolgen zufällig in UTC befinden.
Spaaarky21
201

Okay, diese Frage ist bereits beantwortet, aber ich werde meine Antwort trotzdem fallen lassen. Es könnte jemandem helfen.

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

  • Joda kam nicht in Frage - es ist riesig und leidet unter langsamer Initialisierung. Es schien auch ein großer Overkill für diesen speziellen Zweck zu sein.
  • Antworten, die dies betreffen javax.xml, funktionieren unter Android API 7 nicht.

Endete die Implementierung dieser einfachen Klasse. Es werden nur die gängigsten Formen von ISO 8601-Zeichenfolgen behandelt. Dies sollte jedoch in einigen Fällen ausreichen (wenn Sie sich ziemlich sicher sind, dass die Eingabe in diesem Format erfolgt).

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

Leistungshinweis: Ich instanziiere jedes Mal ein neues SimpleDateFormat, um einen Fehler in Android 2.1 zu vermeiden . Wenn Sie genauso erstaunt sind wie ich, sehen Sie sich dieses Rätsel an . Bei anderen Java-Engines können Sie die Instanz in einem privaten statischen Feld zwischenspeichern (um Thread-sicher zu sein, verwenden Sie ThreadLocal).

Wrygiel
quelle
2
Vielleicht hätte dies zu einer eigenen Frage mit eigener Antwort gemacht werden sollen?
Thorbear
5
Dies war die erste Seite, auf die ich gestoßen bin, als ich nach der Antwort gesucht habe, also schien es passend. Für die meisten Java-Entwickler ist Android nicht genau Java. In den meisten Fällen funktioniert eines jedoch genauso wie das andere, sodass viele Android-Entwickler bei der Suche nach "Java" suchen.
Wrygiel
1
Beachten Sie, dass dies keine Millisekundenauflösung berücksichtigt. Dies ist einfach hinzuzufügen.
Sky Kelsey
6
Ich musste .SSS für Sekundenbruchteile hinzufügen, funktioniert aber großartig, danke. Warum tust du s = s.substring(0, 22) + s.substring(23);- ich sehe den Punkt darin nicht
Dori
1
input = input.replaceAll ("[Zz]", "+0000"); funktioniert auch und Teilstring-Betrieb kann vermieden werden.
Javanator
115

java.time

Die java.time-API (in Java 8 und höher integriert) erleichtert dies ein wenig.

Wenn Sie wissen, dass die Eingabe in UTC erfolgt , z. B. Z(für Zulu) am Ende, kann die InstantKlasse analysieren.

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

Wenn Sie Ihre Eingabe eine andere sein kann , von-UTC - Offset- Werte und nicht UTC durch die angezeigte Z(Zulu) am Ende, verwenden Sie die OffsetDateTimeKlasse zu parsen.

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

Extrahieren Sie dann ein Instantund konvertieren Sie es java.util.Datedurch Aufrufen in ein from.

Instant instant = odt.toInstant();  // Instant is always in UTC.
java.util.Date date = java.util.Date.from( instant );
Adam
quelle
8
Diese Antwort arbeitet zu hart. Ein java.util.Date hat per Definition keine Zeitzone. Der gesamte zeitzonenbezogene Code ist also nicht erforderlich: das LocalDateTimeund ZoneIdund atZone. Dieser einfache Einzeiler wird tun:java.util.Date date = Date.from( ZonedDateTime.parse( "2014-12-12T10:39:40Z" ).toInstant() );
Basil Bourque
5
@BasilBourque Das ist unnötig kompliziert: Date.from(Instant.parse("2014-12-12T10:39:40Z" ));ist genug.
Assylias
3
@assylias Sie sind korrekt, aber das funktioniert nur, wenn die Datumszeichenfolge UTC-Zone ist, ISO8601 erlaubt jede Zeitzone ...
Adam
2
@Adam Mein schlechtes - ich hatte nicht bemerkt, dass die Frage allgemeiner war als Ihr Beispiel. Als OffsetDateTimeNebenkommentar würde ein ausreichen, um ISO8601 zu analysieren (das keine Zeitzoneninformationen, sondern nur einen Versatz enthält).
Assylias
1
@assylias Vielen Dank für Ihren Kommentar Instantzum Parsen. Obwohl dies für diese spezielle Frage nicht ausreicht, ist es eine wichtige Unterscheidung, auf die hingewiesen werden sollte. Also habe ich ein zweites Beispiel für Code hinzugefügt. Hoppla, habe gerade bemerkt, dass dies ursprünglich nicht meine Antwort ist. Ich hoffe Adam stimmt zu.
Basil Bourque
67

Die Jackson-Datenbindungsbibliothek verfügt auch über die ISO8601DateFormat-Klasse , die dies tut (tatsächliche Implementierung in ISO8601Utils) .

ISO8601DateFormat df = new ISO8601DateFormat();
Date d = df.parse("2010-07-28T22:25:51Z");
david_p
quelle
Dieses Datum kann nicht analysiert werden : 2015-08-11T13:10:00. Ich verstehe String index out of range: 19. Wenn man sich den Code ansieht, scheint es, dass die Millisekunden und die Zeitzone angegeben werden müssen. Diese sollten optional sein.
Timmmm
2
Um die Dokumentation zu zitieren, lautet das Analyseformat : [yyyy-MM-dd|yyyyMMdd][T(hh:mm[:ss[.sss]]|hhmm[ss[.sss]])]?[Z|[+-]hh:mm]]. Mit anderen Worten, die Millisekunden sind optional, aber die Zeitzone ist obligatorisch.
David_p
2
Ah ja, eigentlich sieht es so aus, als ob du recht hast. Trotzdem bin ich mir ziemlich sicher, dass Sie mit ISO8601 die Zeitzone weglassen können, sodass sie immer noch falsch ist. JodaTime funktioniert jedoch:new DateTime("2015-08-11T13:10:00").toDate()
Timmmm
3
Diese Klasse ist jetzt veraltet, die neue ist StdDateFormat. Ansonsten funktioniert es genauso.
JohnEye
51

tl; dr

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

Mit java.time

Das neue Paket java.time in Java 8 und höher wurde von Joda-Time inspiriert.

Die OffsetDateTimeKlasse repräsentiert einen Moment auf der Zeitachse mit einem Versatz von UTC, aber keine Zeitzone.

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

Beim Aufrufen toStringwird eine Zeichenfolge im Standardformat ISO 8601 generiert:

2010-01-01T12: 00 + 01: 00

Um den gleichen Wert durch das Objektiv von UTC zu sehen, extrahieren Sie einen Instantoder passen Sie den Versatz von +01:00bis an 00:00.

Instant instant = odt.toInstant();  

…oder…

OffsetDateTime odtUtc = odt.withOffsetSameInstant( ZoneOffset.UTC );

Stellen Sie bei Bedarf eine Zeitzone ein. Eine Zeitzone ist eine Historie von Offset-von-UTC- Werten für eine Region mit einer Reihe von Regeln für die Behandlung von Anomalien wie Sommerzeit (DST). Wenden Sie daher nach Möglichkeit eine Zeitzone anstelle eines bloßen Versatzes an.

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

Ü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?

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
27

Für Java Version 7

Sie können der Oracle-Dokumentation folgen: http://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html

X - wird für die Zeitzone ISO 8601 verwendet

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);
d.danailov
quelle
Dies bedeutet, dass die Zeitzone erforderlich ist . Gemäß ISO 8601 ist es optional. Wie die Sekunden usw. Dies analysiert also nur eine bestimmte Teilmenge von ISO 8601.
Timmmm
1
Funktioniert hervorragend mit Java 1.8
Thiago Pereira
20

Die DatatypeConverter-Lösung funktioniert nicht auf allen VMs. Folgendes funktioniert für mich:

javax.xml.datatype.DatatypeFactory.newInstance().newXMLGregorianCalendar("2011-01-01Z").toGregorianCalendar().getTime()

Ich habe festgestellt, dass joda nicht sofort funktioniert (speziell für das Beispiel, das ich oben mit der Zeitzone an einem Datum angegeben habe, das gültig sein sollte).

James Scriven
quelle
15

Ich denke wir sollten verwenden

DateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")

für Datum 2010-01-01T12:00:00Z

Toby
quelle
5
Warum ist dies eine bessere Antwort als die anderen, einschließlich der akzeptierten Antwort mit 76 positiven Stimmen?
Erick Robertson
3
@ErickRobertson: Es ist einfach, sofort einsatzbereit, flexibel, keine Conversions, und die meisten Leute interessieren sich nicht für Zeitzonen.
TWiStErRob
7
Es macht nicht viel Sinn, mit Zeiten zu arbeiten, wenn Sie sich nicht für Zeitzonen interessieren!
Dori
16
Dies ignoriert die Zeitzone vollständig. Ich habe dies verwendet, bis mir klar wurde, dass dies geschah, und bin zu JodaTime gewechselt.
Joshua Pinter
3
Das Wegwerfen der Zeitzone führt einfach irgendwann zu Fehlern.
Bart van Kuik
10

Eine andere sehr einfache Möglichkeit, ISO8601-Zeitstempel zu analysieren, ist die Verwendung von org.apache.commons.lang.time.DateUtils:

import static org.junit.Assert.assertEquals;

import java.text.ParseException;
import java.util.Date;
import org.apache.commons.lang.time.DateUtils;
import org.junit.Test;

public class ISO8601TimestampFormatTest {
  @Test
  public void parse() throws ParseException {
    Date date = DateUtils.parseDate("2010-01-01T12:00:00+01:00", new String[]{ "yyyy-MM-dd'T'HH:mm:ssZZ" });
    assertEquals("Fri Jan 01 12:00:00 CET 2010", date.toString());
  }
}
tmandry
quelle
10

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);
Antonio
quelle
2
Wenn die Zeichenfolge im Sofortformat vorliegt und Zals Versatz nachgestellt wird , müssen wir dies nicht explizit angeben. Einfach Instant i = Instant.parse(s);. Die Zeichenfolge in der Frage hatte +01:00, in welchem ​​Fall DateTimeFormatter.ISO_INSTANTnicht funktioniert (zumindest nicht auf meinem Java 11).
Ole VV
2
@ OleV.V. Sie können ISO_OFFSET_DATE_TIMEDatumsangaben mit Offsets formatieren, z. B. +01:00( docs.oracle.com/javase/8/docs/api/java/time/format/… )
Lucas Basquerotto,
1
Das stimmt, @LucasBasquerotto. Obwohl dieser Formatierer nicht explizit erwähnt wird, tun die Antworten von Adam und Basil Bourque bereits etwas Ähnliches.
Ole VV
6

java.time

Beachten Sie, dass Sie in Java 8 die Klasse java.time.ZonedDateTime und ihre statische parse(CharSequence text)Methode verwenden können.

Martin Rust
quelle
Die Eingabezeichenfolgen in der Frage haben nur einen Versatz von UTC, keine Vollzeitzone. Also Instantund ZonedDateTimesind hier nicht angebracht ZonedDateTime.
Basil Bourque
6

Die Problemumgehung für Java 7+ verwendet SimpleDateFormat:
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX", Locale.US);

Dieser Code kann das ISO8601-Format wie folgt analysieren:

  • 2017-05-17T06:01:43.785Z
  • 2017-05-13T02:58:21.391+01:00

Aber auf Java6 SimpleDateFormatversteht nicht XZeichen und wird werfen
IllegalArgumentException: Unknown pattern character 'X'
Wir müssen das ISO8601-Datum auf das Format normalisieren, das in Java 6 mit lesbar ist SimpleDateFormat.

public static Date iso8601Format(String formattedDate) throws ParseException {
    try {
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX", Locale.US);
        return df.parse(formattedDate);
    } catch (IllegalArgumentException ex) {
        // error happen in Java 6: Unknown pattern character 'X'
        if (formattedDate.endsWith("Z")) formattedDate = formattedDate.replace("Z", "+0000");
        else formattedDate = formattedDate.replaceAll("([+-]\\d\\d):(\\d\\d)\\s*$", "$1$2");
        DateFormat df1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.US);
        return df1.parse(formattedDate);
    }
}

Die oben beschriebene Methode ersetzt [ Zmit +0000] oder [ +01:00mit +0100], wenn in Java 6 ein Fehler auftritt (Sie können die Java-Version erkennen und try / catch durch die if-Anweisung ersetzen).

Khang .NT
quelle
Nein, die lästigen alten Datums- und Uhrzeitklassen wie Dateund SimpleDateFormatsind schlecht gestaltet, verwirrend und fehlerhaft. Sie sind jetzt Legacy und werden durch die in Java 8 und höher integrierten java.time-Klassen ersetzt. Für Java 6 und Java 7 wird ein Großteil der Funktionen von java.time im ThreeTen-Backport- Projekt zurückportiert . Es ist viel besser, diese Bibliothek zu Ihrer App hinzuzufügen, als diese älteren Klassen zu verwenden. Einzeilige Lösung in Java.time:OffsetDateTime.parse( "2010-01-01T12:00:00+01:00" )
Basil Bourque
5

Ich hatte das gleiche Problem und löste es mit dem folgenden Code.

 public static Calendar getCalendarFromISO(String datestring) {
    Calendar calendar = Calendar.getInstance(TimeZone.getDefault(), Locale.getDefault()) ;
    SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault());
    try {
        Date date = dateformat.parse(datestring);
        date.setHours(date.getHours() - 1);
        calendar.setTime(date);

        String test = dateformat.format(calendar.getTime());
        Log.e("TEST_TIME", test);

    } catch (ParseException e) {
        e.printStackTrace();
    }

    return calendar;
}

Früher habe ich benutzt SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.getDefault());

Aber später fand ich die Hauptursache für die Ausnahme war die yyyy-MM-dd'T'HH:mm:ss.SSSZ,

Also habe ich benutzt

SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault());

Es hat gut für mich funktioniert.

Abhay Kumar
quelle
Genau das, was ich brauchte, ohne joda-time, XML api oder irgendetwas anderes verwenden zu müssen. Genau das richtige Muster.
Philippe Gioseffi
4

Java hat ein Dutzend verschiedene Möglichkeiten, eine Datums- und Uhrzeitangabe zu analysieren, wie die hervorragenden Antworten hier zeigen. Aber etwas erstaunlicherweise implementiert keine der Zeitklassen von Java ISO 8601 vollständig!

Mit Java 8 würde ich empfehlen:

ZonedDateTime zp = ZonedDateTime.parse(string);
Date date = Date.from(zp.toInstant());

Dies behandelt Beispiele sowohl in UTC als auch mit einem Versatz wie "2017-09-13T10: 36: 40Z" oder "2017-09-13T10: 36: 40 + 01: 00". Dies ist für die meisten Anwendungsfälle ausreichend.

Aber es wird nicht Beispiele wie behandeln „2017-09-13T10: 36: 40 + 01“, das ist eine gültige ISO 8601 Datum-Zeit.
Es wird auch nicht nur das Datum behandelt, z. B. "2017-09-13".

Wenn Sie damit umgehen müssen, würde ich vorschlagen, zuerst einen regulären Ausdruck zu verwenden, um die Syntax zu riechen.

Es gibt hier eine schöne Liste von ISO 8601-Beispielen mit vielen Eckfällen: https://www.myintervals.com/blog/2009/05/20/iso-8601-date-validation-that-doesnt-suck/ I'm Keine Java-Klasse bekannt, die mit allen fertig werden könnte.

Daniel Winterstein
quelle
OffsetDateTimewird tun und konzeptionell eine Datums- und Uhrzeitangabe mit einem Versatz besser abgleichen.
Ole VV
Hey @ OleV.V. danke für den Vorschlag. Leider nein: OffsetDateTime.parse () löst eine Ausnahme für mehrere gültige ISO 8601-Zeichenfolgen aus, z. B. "2017-09-13T10: 36: 40 + 01" oder "2017-09-13"
Daniel Winterstein,
Ich wollte nur sagen, dass dies OffsetDateTimedie Beispiele behandelt, mit denen Sie umgehen ZonedDateTime. Ich glaube, dass es keines der Beispiele behandelt, die es nicht tun ZonedDateTime. In diesem Sinne ist es keine Verbesserung (aber auch keine schlechtere). Entschuldigung, ich war nicht ganz klar.
Ole VV
1
Dies sollte angesichts des aktuellen Zustands die akzeptierte Antwort im Jahr 2020 sein.
slashCoder
3

Apache Jackrabbit verwendet das ISO 8601-Format, um Daten zu speichern, und es gibt eine Hilfsklasse, um sie zu analysieren:

org.apache.jackrabbit.util.ISO8601

Kommt mit Jackrabbit-Jcr-Commons .

Alexander Klimetschek
quelle
Während die Teilmenge von Jackrabbit möglicherweise funktioniert, ist es sinnvoller, eine vollwertige, speziell erstellte Bibliothek zu verwenden. In Java bedeutet das entweder Joda-Time oder java.time.
Basil Bourque
3

Wie bereits erwähnt, bietet Android keine gute Möglichkeit, das Parsen / Formatieren von ISO 8601-Daten mithilfe der im SDK enthaltenen Klassen zu unterstützen. Ich habe diesen Code mehrmals geschrieben, also habe ich endlich eine Liste erstellt, die eine DateUtils-Klasse enthält, die das Formatieren und Parsen von ISO 8601- und RFC 1123-Daten unterstützt. Das Gist enthält auch einen Testfall, der zeigt, was es unterstützt.

https://gist.github.com/mraccola/702330625fad8eebe7d3

Matt Accola
quelle
2

SimpleDateFormat für JAVA 1.7 hat ein cooles Muster für das ISO 8601-Format.

Klasse SimpleDateFormat

Folgendes habe ich getan:

Date d = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss.SSSZ",
         Locale.ENGLISH).format(System.currentTimeMillis());
Eesha
quelle
2
Zim Format Zeichenfolge ist nicht ISO 8601 Zeitzone, sollten Sie verwenden X(oder XXoder XXX), wenn Sie ISO 8601 Zeitzone wollen
Vojta
d ist vom Typ String
Tim Child
1

Mach es so:

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

    String dateStr = "2016-10-19T14:15:36+08:00";
    Date date = javax.xml.bind.DatatypeConverter.parseDateTime(dateStr).getTime();

    System.out.println(date);

}

Hier ist die Ausgabe:

Mi 19.10. 15:15:36 CST 2016

Yinhaomin
quelle
1

Verwenden Sie string like LocalDate.parse(((String) data.get("d_iso8601")),DateTimeFormatter.ISO_DATE)

Stepan
quelle
1

Ich bin überrascht, dass nicht einmal eine Java-Bibliothek alle ISO 8601-Datumsformate gemäß https://en.wikipedia.org/wiki/ISO_8601 unterstützt . Joda DateTime unterstützte die meisten von ihnen, aber nicht alle, und daher habe ich eine benutzerdefinierte Logik hinzugefügt, um alle zu behandeln. Hier ist meine Implementierung.

import java.text.ParseException;
import java.util.Date;

import org.apache.commons.lang3.time.DateUtils;
import org.joda.time.DateTime;

public class ISO8601DateUtils {
	
	/**
	 * It parses all the date time formats from https://en.wikipedia.org/wiki/ISO_8601 and returns Joda DateTime.
	 * Zoda DateTime does not support dates of format 20190531T160233Z, and hence added custom logic to handle this using SimpleDateFormat.
	 * @param dateTimeString ISO 8601 date time string
	 * @return
	 */
	public static DateTime parse(String dateTimeString) {
		try {
			return new DateTime( dateTimeString );
		} catch(Exception e) {
			try {
				Date dateTime = DateUtils.parseDate(dateTimeString, JODA_NOT_SUPPORTED_ISO_DATES);
				return new DateTime(dateTime.getTime());
			} catch (ParseException e1) {
				throw new RuntimeException(String.format("Date %s could not be parsed to ISO date", dateTimeString));
			}
		}
	}
  
  	private static String[] JODA_NOT_SUPPORTED_ISO_DATES = new String[] {
			// upto millis
			"yyyyMMdd'T'HHmmssSSS'Z'",
			"yyyyMMdd'T'HHmmssSSSZ",
			"yyyyMMdd'T'HHmmssSSSXXX",
			
			"yyyy-MM-dd'T'HHmmssSSS'Z'",
			"yyyy-MM-dd'T'HHmmssSSSZ",
			"yyyy-MM-dd'T'HHmmssSSSXXX",
			
			// upto seconds
			"yyyyMMdd'T'HHmmss'Z'",
			"yyyyMMdd'T'HHmmssZ",
			"yyyyMMdd'T'HHmmssXXX",
			
			"yyyy-MM-dd'T'HHmmss'Z'", 
			"yyyy-MM-dd'T'HHmmssZ",
			"yyyy-MM-dd'T'HHmmssXXX",
			
			// upto minutes
			"yyyyMMdd'T'HHmm'Z'",
			"yyyyMMdd'T'HHmmZ",
			"yyyyMMdd'T'HHmmXXX",

			"yyyy-MM-dd'T'HHmm'Z'",
			"yyyy-MM-dd'T'HHmmZ",
			"yyyy-MM-dd'T'HHmmXXX",
			
			//upto hours is already supported by Joda DateTime
	};
}

raok1997
quelle
1

Ein kleiner Test, der zeigt, wie ein Datum in ISO8601 analysiert wird und dass LocalDateTime keine Sommerzeiten verarbeitet.

 @Test
    public void shouldHandleDaylightSavingTimes() throws ParseException {

        //ISO8601 UTC date format
        SimpleDateFormat utcFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");

        // 1 hour of difference between 2 dates in UTC happening at the Daylight Saving Time
        Date d1 = utcFormat.parse("2019-10-27T00:30:00.000Z");
        Date d2 = utcFormat.parse("2019-10-27T01:30:00.000Z");

        //Date 2 is before date 2
        Assert.assertTrue(d1.getTime() < d2.getTime());
        // And there is 1 hour difference between the 2 dates
        Assert.assertEquals(1000*60*60, d2.getTime() - d1.getTime());

        //Print the dates in local time
        SimpleDateFormat localFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm z Z", Locale.forLanguageTag("fr_CH"));
        localFormat.setTimeZone(TimeZone.getTimeZone("Europe/Zurich"));

        //Both dates are at 02h30 local time (because of DST), but one is CEST +0200 and the other CET +0100 (clock goes backwards)
        Assert.assertEquals("2019-10-27 02:30 CEST +0200", localFormat.format(d1));
        Assert.assertEquals("2019-10-27 02:30 CET +0100", localFormat.format(d2));

        //Small test that shows that LocalDateTime does not handle DST (and should not be used for storing timeseries data)
        LocalDateTime ld1 = LocalDateTime.ofInstant(d1.toInstant(), ZoneId.of("Europe/Zurich"));
        LocalDateTime ld2 = LocalDateTime.ofInstant(d2.toInstant(), ZoneId.of("Europe/Zurich"));

        //Note that a localdatetime does not handle DST, therefore the 2 dates are the same
        Assert.assertEquals(ld1, ld2);

        //They both have the following local values
        Assert.assertEquals(2019, ld1.getYear());
        Assert.assertEquals(27, ld1.getDayOfMonth());
        Assert.assertEquals(10, ld1.getMonthValue());
        Assert.assertEquals(2, ld1.getHour());
        Assert.assertEquals(30, ld1.getMinute());
        Assert.assertEquals(0, ld1.getSecond());

    }
ddtxra
quelle
3
Zu Ihrer Information, die furchtbar mühsam Datum Zeitklassen wie java.util.Date, java.util.Calendarund java.text.SimpleDateFormatsind jetzt Erbe , durch die verdrängte java.time Klassen in Java gebaut 8 und höher. Siehe Tutorial von Oracle .
Basil Bourque
Sie haben Recht, dass LocalDateTimedie Sommerzeit (DST) nicht behandelt wird, da sie überhaupt keine Zeitzone behandelt. Dafür brauchen wir ZonedDateTime. Vorschlagen Dateund SimpleDateFormatist - IMHO schlecht.
Ole VV
1
In der Tat funktioniert ZonedDateTime. Und java.time.Instant ist auch eine gute Alternative zur Sommerzeit. Ich weiß, dass java.util.Date veraltet ist und nicht verwendet werden sollte, aber ich habe gerade die ursprüngliche Frage beantwortet: Wie man einen String in 8601 in java.util.date konvertiert ....
ddtxra
0

Ich hatte ein ähnliches Bedürfnis: Ich musste in der Lage sein, jedes ISO8601-konforme Datum zu analysieren, ohne vorher das genaue Format zu kennen, und ich wollte eine leichte Lösung, die auch unter Android funktioniert.

Als ich meine Bedürfnisse googelte, stolperte ich über diese Frage und bemerkte, dass AFAIU, keine Antwort vollständig meinen Bedürfnissen entsprach. Also habe ich jISO8601 entwickelt und auf Maven Central geschoben.

Fügen Sie einfach hinzu pom.xml:

<dependency>
  <groupId>fr.turri</groupId>
  <artifactId>jISO8601</artifactId>
  <version>0.2</version>
</dependency>

und dann können Sie loslegen:

import fr.turri.jiso8601.*;
...
Calendar cal = Iso8601Deserializer.toCalendar("1985-03-04");
Date date = Iso8601Deserializer.toDate("1985-03-04T12:34:56Z");

Hoffe es hilft.

gturri
quelle
0

Um nur ein Datum wie dieses zu formatieren, hat das Folgende für mich in einer Java 6-basierten Anwendung funktioniert. Es gibt eine DateFormatKlasse JacksonThymeleafISO8601DateFormatim Thymeleaf-Projekt, die den fehlenden Doppelpunkt einfügt:

https://github.com/thymeleaf/thymeleaf/blob/40d27f44df7b52eda47d1bc6f1b3012add6098b3/src/main/java/org/thymeleaf/standard/serializer/StandardJavaScriptSerializer.java

Ich habe es für die Kompatibilität mit dem ECMAScript-Datumsformat verwendet.

ekip
quelle
-1

Basisfunktion mit freundlicher Genehmigung von @wrygiel.

Diese Funktion kann das ISO8601-Format in Java-Datum konvertieren, das die Versatzwerte verarbeiten kann. Gemäß der Definition von ISO 8601 kann der Offset in verschiedenen Formaten angegeben werden.

±[hh]:[mm]
±[hh][mm]
±[hh]

Eg:  "18:30Z", "22:30+04", "1130-0700", and "15:00-03:30" all mean the same time. - 06:30PM UTC

Diese Klasse verfügt über statische Methoden zum Konvertieren

  • ISO8601-Zeichenfolge zum Datumsobjekt (Local TimeZone)
  • Datum der ISO8601-Zeichenfolge
  • Die Sommerzeit wird automatisch berechnet

Beispiel ISO8601 Strings

/*       "2013-06-25T14:00:00Z";
         "2013-06-25T140000Z";
         "2013-06-25T14:00:00+04";
         "2013-06-25T14:00:00+0400";
         "2013-06-25T140000+0400";
         "2013-06-25T14:00:00-04";
         "2013-06-25T14:00:00-0400";
         "2013-06-25T140000-0400";*/


public class ISO8601DateFormatter {

private static final DateFormat DATE_FORMAT_1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
private static final DateFormat DATE_FORMAT_2 = new SimpleDateFormat("yyyy-MM-dd'T'HHmmssZ");
private static final String UTC_PLUS = "+";
private static final String UTC_MINUS = "-";

public static Date toDate(String iso8601string) throws ParseException {
    iso8601string = iso8601string.trim();
    if(iso8601string.toUpperCase().indexOf("Z")>0){
        iso8601string = iso8601string.toUpperCase().replace("Z", "+0000");
    }else if(((iso8601string.indexOf(UTC_PLUS))>0)){
        iso8601string = replaceColon(iso8601string, iso8601string.indexOf(UTC_PLUS));
        iso8601string = appendZeros(iso8601string, iso8601string.indexOf(UTC_PLUS), UTC_PLUS);
    }else if(((iso8601string.indexOf(UTC_MINUS))>0)){
        iso8601string = replaceColon(iso8601string, iso8601string.indexOf(UTC_MINUS));
        iso8601string = appendZeros(iso8601string, iso8601string.indexOf(UTC_MINUS), UTC_MINUS);
    }

    Date date = null;
    if(iso8601string.contains(":"))
        date = DATE_FORMAT_1.parse(iso8601string);
    else{
        date = DATE_FORMAT_2.parse(iso8601string);
    }
    return date;
}

public static String toISO8601String(Date date){
    return DATE_FORMAT_1.format(date);
}

private static String replaceColon(String sourceStr, int offsetIndex){
    if(sourceStr.substring(offsetIndex).contains(":"))
        return sourceStr.substring(0, offsetIndex) + sourceStr.substring(offsetIndex).replace(":", "");
    return sourceStr;
}

private static String appendZeros(String sourceStr, int offsetIndex, String offsetChar){
    if((sourceStr.length()-1)-sourceStr.indexOf(offsetChar,offsetIndex)<=2)
        return sourceStr + "00";
    return sourceStr;
}

}}

AKh
quelle
2
Achtung - DateFormat und abgeleitete Klassen sind nicht Multithread-kompatibel! Die Verwendung statischer SimpleDateFormat-Objekte wie DATE_FORMAT_1 und DATE_FORMAT_2 bedeutet, dass mehrere Threads, die die ISO8601DateFormatter-Funktionen aufrufen, dasselbe DateFormat-Objekt verwenden. Dies führt zu Datenbeschädigungen und falschen Daten, die von den DateFormat-Aufrufen zurückgegeben werden. Um dies zu beheben, sollten Sie einfach die Musterzeichenfolgenkonstanten festlegen und bei Bedarf lokale SimpleDateFormat-Variablen erstellen. Dadurch wird sichergestellt, dass jedes Objekt nur von einem Thread verwendet wird.
Theo
Eine bessere Lösung für die Thread-Sicherheit besteht darin, stattdessen eine Datums- / Uhrzeitbibliothek zu verwenden, die für die Thread-Sicherheit erstellt wurde. In Java ist diese Welt entweder Joda-Time oder java.time.
Basil Bourque
-1

Das schien für mich am besten zu funktionieren:

public static Date fromISO8601_( String string ) {

    try {
            return new SimpleDateFormat ( "yyyy-MM-dd'T'HH:mm:ssXXX").parse ( string );
    } catch ( ParseException e ) {
        return Exceptions.handle (Date.class, "Not a valid ISO8601", e);
    }


}

Ich musste JavaScript-Datumszeichenfolgen in Java konvertieren. Ich fand die obigen Arbeiten mit der Empfehlung. Es gab einige Beispiele mit SimpleDateFormat, die nahe beieinander lagen, aber nicht die Teilmenge zu sein schienen, wie empfohlen von:

http://www.w3.org/TR/NOTE-datetime

und unterstützt von PLIST und JavaScript Strings und so was ich brauchte.

Dies scheint die häufigste Form von ISO8601-Zeichenfolgen zu sein und eine gute Teilmenge.

Die Beispiele, die sie geben, sind:

1994-11-05T08:15:30-05:00 corresponds 
November 5, 1994, 8:15:30 am, US Eastern Standard Time.

 1994-11-05T13:15:30Z corresponds to the same instant.

Ich habe auch eine schnelle Version:

final static int SHORT_ISO_8601_TIME_LENGTH =  "1994-11-05T08:15:30Z".length ();
                                            // 01234567890123456789012
final static int LONG_ISO_8601_TIME_LENGTH = "1994-11-05T08:15:30-05:00".length ();


public static Date fromISO8601( String string ) {
    if (isISO8601 ( string )) {
        char [] charArray = Reflection.toCharArray ( string );//uses unsafe or string.toCharArray if unsafe is not available
        int year = CharScanner.parseIntFromTo ( charArray, 0, 4 );
        int month = CharScanner.parseIntFromTo ( charArray, 5, 7 );
        int day = CharScanner.parseIntFromTo ( charArray, 8, 10 );
        int hour = CharScanner.parseIntFromTo ( charArray, 11, 13 );

        int minute = CharScanner.parseIntFromTo ( charArray, 14, 16 );

        int second = CharScanner.parseIntFromTo ( charArray, 17, 19 );

        TimeZone tz ;

         if (charArray[19] == 'Z') {

             tz = TimeZone.getTimeZone ( "GMT" );
         } else {

             StringBuilder builder = new StringBuilder ( 9 );
             builder.append ( "GMT" );
             builder.append( charArray, 19, LONG_ISO_8601_TIME_LENGTH - 19);
             String tzStr = builder.toString ();
             tz = TimeZone.getTimeZone ( tzStr ) ;

         }
         return toDate ( tz, year, month, day, hour, minute, second );

    }   else {
        return null;
    }

}

...

public static int parseIntFromTo ( char[] digitChars, int offset, int to ) {
    int num = digitChars[ offset ] - '0';
    if ( ++offset < to ) {
        num = ( num * 10 ) + ( digitChars[ offset ] - '0' );
        if ( ++offset < to ) {
            num = ( num * 10 ) + ( digitChars[ offset ] - '0' );
            if ( ++offset < to ) {
                num = ( num * 10 ) + ( digitChars[ offset ] - '0' );
                if ( ++offset < to ) {
                    num = ( num * 10 ) + ( digitChars[ offset ] - '0' );
                    if ( ++offset < to ) {
                        num = ( num * 10 ) + ( digitChars[ offset ] - '0' );
                        if ( ++offset < to ) {
                            num = ( num * 10 ) + ( digitChars[ offset ] - '0' );
                            if ( ++offset < to ) {
                                num = ( num * 10 ) + ( digitChars[ offset ] - '0' );
                                if ( ++offset < to ) {
                                    num = ( num * 10 ) + ( digitChars[ offset ] - '0' );
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    return num;
}


public static boolean isISO8601( String string ) {
      boolean valid = true;

      if (string.length () == SHORT_ISO_8601_TIME_LENGTH) {
          valid &=  (string.charAt ( 19 )  == 'Z');

      } else if (string.length () == LONG_ISO_8601_TIME_LENGTH) {
          valid &=  (string.charAt ( 19 )  == '-' || string.charAt ( 19 )  == '+');
          valid &=  (string.charAt ( 22 )  == ':');

      } else {
          return false;
      }

    //  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4
    // "1 9 9 4 - 1 1 - 0 5 T 0 8 : 1 5 : 3 0 - 0 5 : 0 0

    valid &=  (string.charAt ( 4 )  == '-') &&
                (string.charAt ( 7 )  == '-') &&
                (string.charAt ( 10 ) == 'T') &&
                (string.charAt ( 13 ) == ':') &&
                (string.charAt ( 16 ) == ':');

    return valid;
}

Ich habe es nicht bewertet, aber ich denke, es wird ziemlich schnell gehen. Es scheint zu funktionieren. :) :)

@Test
public void testIsoShortDate() {
    String test =  "1994-11-05T08:15:30Z";

    Date date = Dates.fromISO8601 ( test );
    Date date2 = Dates.fromISO8601_ ( test );

    assertEquals(date2.toString (), date.toString ());

    puts (date);
}

@Test
public void testIsoLongDate() {
    String test =  "1994-11-05T08:11:22-05:00";

    Date date = Dates.fromISO8601 ( test );
    Date date2 = Dates.fromISO8601_ ( test );

    assertEquals(date2.toString (), date.toString ());

    puts (date);
}
RickHigh
quelle
-2

Ich denke, was viele Leute tun wollen, ist JSON-Datumszeichenfolgen zu analysieren. Wenn Sie zu dieser Seite gelangen, besteht eine gute Chance, dass Sie ein JavaScript-JSON-Datum in ein Java-Datum konvertieren möchten.

So zeigen Sie, wie eine JSON-Datumszeichenfolge aussieht:

    var d=new Date();
    var s = JSON.stringify(d);

    document.write(s);
    document.write("<br />"+d);


    "2013-12-14T01:55:33.412Z"
    Fri Dec 13 2013 17:55:33 GMT-0800 (PST)

Die JSON-Datumszeichenfolge lautet 2013-12-14T01: 55: 33.412Z.

Daten werden nicht von der JSON-Spezifikation abgedeckt, aber das oben genannte ist ein sehr spezifisches ISO 8601-Format, während ISO_8601 viel viel größer ist und dies nur eine Teilmenge ist, wenn auch eine sehr wichtige.

Siehe http://www.json.org Siehe http://en.wikipedia.org/wiki/ISO_8601 Siehe http://www.w3.org/TR/NOTE-datetime

Zufällig habe ich einen JSON-Parser und einen PLIST-Parser geschrieben, die beide ISO-8601 verwenden, aber nicht dieselben Bits.

/*
    var d=new Date();
    var s = JSON.stringify(d);

    document.write(s);
    document.write("<br />"+d);


    "2013-12-14T01:55:33.412Z"
    Fri Dec 13 2013 17:55:33 GMT-0800 (PST)


 */
@Test
public void jsonJavaScriptDate() {
    String test =  "2013-12-14T01:55:33.412Z";

    Date date = Dates.fromJsonDate ( test );
    Date date2 = Dates.fromJsonDate_ ( test );

    assertEquals(date2.toString (), "" + date);

    puts (date);
}

Ich habe zwei Möglichkeiten geschrieben, dies für mein Projekt zu tun. Ein Standard, einer schnell.

Auch hier ist die JSON-Datumszeichenfolge eine sehr spezifische Implementierung von ISO 8601 ....

(Ich habe die andere in der anderen Antwort gepostet, die für PLIST-Daten funktionieren sollte, die ein anderes ISO 8601-Format haben.)

Das JSON-Datum lautet wie folgt:

public static Date fromJsonDate_( String string ) {

    try {

        return new SimpleDateFormat ( "yyyy-MM-dd'T'HH:mm:ss.SSSXXX").parse ( string );
    } catch ( ParseException e ) {
        return Exceptions.handle (Date.class, "Not a valid JSON date", e);
    }


}

PLIST-Dateien (ASCII non GNUNext) verwenden ebenfalls ISO 8601, jedoch keine Millisekunden. Daher sind nicht alle ISO-8601-Daten gleich. (Zumindest habe ich noch keinen gefunden, der Milis verwendet, und der Parser, den ich gesehen habe, überspringt die Zeitzone insgesamt OMG).

Nun zur schnellen Version (Sie finden sie in Boon).

public static Date fromJsonDate( String string ) {

    return fromJsonDate ( Reflection.toCharArray ( string ), 0, string.length () );

}

Beachten Sie, dass Reflection.toCharArray unsicher verwendet, wenn verfügbar, aber standardmäßig string.toCharArray, wenn nicht.

(Sie können es aus dem Beispiel entfernen, indem Sie Reflection.toCharArray (Zeichenfolge) durch Zeichenfolge.toCharArray () ersetzen.)

public static Date fromJsonDate( char[] charArray, int from, int to ) {

    if (isJsonDate ( charArray, from, to )) {
        int year = CharScanner.parseIntFromTo ( charArray, from + 0, from + 4 );
        int month = CharScanner.parseIntFromTo ( charArray,  from +5,  from +7 );
        int day = CharScanner.parseIntFromTo ( charArray,  from +8,  from +10 );
        int hour = CharScanner.parseIntFromTo ( charArray,  from +11,  from +13 );

        int minute = CharScanner.parseIntFromTo ( charArray,  from +14,  from +16 );

        int second = CharScanner.parseIntFromTo ( charArray,  from +17,  from +19 );

        int miliseconds = CharScanner.parseIntFromTo ( charArray,  from +20,  from +23 );

        TimeZone tz = TimeZone.getTimeZone ( "GMT" );


        return toDate ( tz, year, month, day, hour, minute, second, miliseconds );

    }   else {
        return null;
    }

}

Das isJsonDate wird wie folgt implementiert:

public static boolean isJsonDate( char[] charArray, int start, int to ) {
    boolean valid = true;
    final int length = to -start;

    if (length != JSON_TIME_LENGTH) {
        return false;
    }

    valid &=  (charArray [ start + 19 ]  == '.');

    if (!valid) {
        return false;
    }


    valid &=  (charArray[  start +4 ]  == '-') &&
            (charArray[  start +7 ]  == '-') &&
            (charArray[  start +10 ] == 'T') &&
            (charArray[  start +13 ] == ':') &&
            (charArray[  start +16 ] == ':');

    return valid;
}

Wie auch immer ... ich vermute, dass einige Leute, die hierher kommen, nach dem JSON-Datumsstring suchen und obwohl es sich um ein ISO-8601-Datum handelt, ist es ein sehr spezifisches Datum, das eine ganz bestimmte Analyse benötigt.

public static int parseIntFromTo ( char[] digitChars, int offset, int to ) {
    int num = digitChars[ offset ] - '0';
    if ( ++offset < to ) {
        num = ( num * 10 ) + ( digitChars[ offset ] - '0' );
        if ( ++offset < to ) {
            num = ( num * 10 ) + ( digitChars[ offset ] - '0' );
            if ( ++offset < to ) {
                num = ( num * 10 ) + ( digitChars[ offset ] - '0' );
                if ( ++offset < to ) {
                    num = ( num * 10 ) + ( digitChars[ offset ] - '0' );
                    if ( ++offset < to ) {
                        num = ( num * 10 ) + ( digitChars[ offset ] - '0' );
                        if ( ++offset < to ) {
                            num = ( num * 10 ) + ( digitChars[ offset ] - '0' );
                            if ( ++offset < to ) {
                                num = ( num * 10 ) + ( digitChars[ offset ] - '0' );
                                if ( ++offset < to ) {
                                    num = ( num * 10 ) + ( digitChars[ offset ] - '0' );
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    return num;
}

Siehe https://github.com/RichardHightower/boon Boon verfügt über einen PLIST-Parser (ASCII) und einen JSON-Parser.

Der JSON-Parser ist der schnellste mir bekannte Java-JSON-Parser.

Unabhängig von den Jungs von Gatling Performance verifiziert.

https://github.com/gatling/json-parsers-benchmark

Benchmark                               Mode Thr     Count  Sec         Mean   Mean error        Units
BoonCharArrayBenchmark.roundRobin      thrpt  16        10    1   724815,875    54339,825    ops/s
JacksonObjectBenchmark.roundRobin      thrpt  16        10    1   580014,875   145097,700    ops/s
JsonSmartBytesBenchmark.roundRobin     thrpt  16        10    1   575548,435    64202,618    ops/s
JsonSmartStringBenchmark.roundRobin    thrpt  16        10    1   541212,220    45144,815    ops/s
GSONStringBenchmark.roundRobin         thrpt  16        10    1   522947,175    65572,427    ops/s
BoonDirectBytesBenchmark.roundRobin    thrpt  16        10    1   521528,912    41366,197    ops/s
JacksonASTBenchmark.roundRobin         thrpt  16        10    1   512564,205   300704,545    ops/s
GSONReaderBenchmark.roundRobin         thrpt  16        10    1   446322,220    41327,496    ops/s
JsonSmartStreamBenchmark.roundRobin    thrpt  16        10    1   276399,298   130055,340    ops/s
JsonSmartReaderBenchmark.roundRobin    thrpt  16        10    1    86789,825    17690,031    ops/s

Es verfügt über den schnellsten JSON-Parser für Streams, Reader, Bytes [], char [], CharSequence (StringBuilder, CharacterBuffer) und String.

Weitere Benchmarks finden Sie unter:

https://github.com/RichardHightower/json-parsers-benchmark

RickHigh
quelle
Diese Antwort zu JSON ist nicht Gegenstand der Frage. Darüber hinaus ist diese Frage falsch, da es unter den wenigen JSON-Datentypen kein „JSON-Datum“ gibt . Und heutzutage kann all dieser Code durch einen einzeiligen Aufruf der integrierten Java-Funktion ersetzt werden:Instant.parse( "2013-12-14T01:55:33.412Z" )
Basil Bourque