Das Parsen eines Strings mit Datum und Uhrzeit zu einem bestimmten Zeitpunkt (Java nennt es ein " Instant
") ist ziemlich kompliziert. Java hat dies in mehreren Iterationen angegangen. Die neueste ein, java.time
und java.time.chrono
, deckt nahezu alle Bedürfnisse (außer Zeitdilatation :)).
Diese Komplexität bringt jedoch viel Verwirrung.
Der Schlüssel zum Verständnis der Datumsanalyse ist:
Warum hat Java so viele Möglichkeiten, ein Datum zu analysieren?
- Es gibt mehrere Systeme zum Messen einer Zeit. Zum Beispiel wurden die historischen japanischen Kalender aus den Zeitbereichen der Regierungszeit des jeweiligen Kaisers oder der jeweiligen Dynastie abgeleitet. Dann gibt es zB UNIX-Zeitstempel. Glücklicherweise hat es die ganze (Geschäfts-) Welt geschafft, dasselbe zu nutzen.
- In der Vergangenheit wurden die Systeme aus verschiedenen Gründen von / nach umgeschaltet . ZB vom julianischen Kalender zum gregorianischen Kalender im Jahr 1582. "Westliche" Daten davor müssen also anders behandelt werden.
- Und natürlich geschah die Änderung nicht sofort. Da der Kalender aus den Hauptquartieren einiger Religionen und anderer Teile Europas stammte, die an andere Ernährungsgewohnheiten glaubten, wechselte beispielsweise Deutschland erst im Jahr 1700.
... und warum ist das LocalDateTime
, ZonedDateTime
et al. so kompliziert
Es gibt Zeitzonen . Eine Zeitzone ist im Grunde ein "Streifen" * [1] der Erdoberfläche, dessen Autoritäten den gleichen Regeln folgen, wann sie welchen Zeitversatz hat. Dies beinhaltet Sommerzeitregeln.
Die Zeitzonen ändern sich im Laufe der Zeit für verschiedene Bereiche, hauptsächlich basierend darauf, wer wen erobert. Und die Regeln einer Zeitzone ändern sich auch im Laufe der Zeit .
Es gibt Zeitversätze. Das ist nicht dasselbe wie Zeitzonen, weil eine Zeitzone zB "Prag" sein kann, aber das hat Sommerzeitversatz und Winterzeitversatz.
Wenn Sie einen Zeitstempel mit einer Zeitzone erhalten, kann der Versatz variieren, je nachdem, in welchem Teil des Jahres er sich befindet. Während der Schaltstunde kann der Zeitstempel zwei verschiedene Zeiten bedeuten, sodass er ohne zusätzliche Informationen nicht zuverlässig sein kann umgewandelt.
Hinweis: Mit Zeitstempel meine ich "eine Zeichenfolge, die ein Datum und / oder eine Uhrzeit enthält, optional mit einer Zeitzone und / oder einem Zeitversatz."
Mehrere Zeitzonen können für bestimmte Zeiträume denselben Zeitversatz verwenden. Beispielsweise entspricht die GMT / UTC-Zeitzone der Zeitzone "London", wenn der Sommerzeitversatz nicht wirksam ist.
Um es etwas komplizierter zu machen (aber das ist für Ihren Anwendungsfall nicht zu wichtig):
- Die Wissenschaftler beobachten die Dynamik der Erde, die sich im Laufe der Zeit ändert. basierend darauf addieren sie am Ende einzelner Jahre Sekunden. (
2040-12-31 24:00:00
Möglicherweise handelt es sich also um eine gültige Datums- und Uhrzeitangabe.) Dies erfordert regelmäßige Aktualisierungen der Metadaten, die Systeme verwenden, um die Datumskonvertierungen richtig durchzuführen. Unter Linux erhalten Sie beispielsweise regelmäßig Updates für die Java-Pakete, einschließlich dieser neuen Daten.
Die Aktualisierungen behalten nicht immer das vorherige Verhalten für historische und zukünftige Zeitstempel bei. Daher kann es vorkommen, dass das Parsen der beiden Zeitstempel um die Änderung einer Zeitzone herum, wenn sie verglichen werden , zu unterschiedlichen Ergebnissen führt, wenn sie auf verschiedenen Versionen der Software ausgeführt werden. Dies gilt auch für den Vergleich zwischen der betroffenen Zeitzone und einer anderen Zeitzone.
Sollte dies zu einem Fehler in Ihrer Software führen, sollten Sie einen Zeitstempel verwenden, für den keine so komplizierten Regeln gelten, z. B. einen UNIX-Zeitstempel .
Aufgrund von 7 können wir für zukünftige Daten Daten nicht mit Sicherheit genau konvertieren. So kann beispielsweise die aktuelle Analyse von 8524-02-17 12:00:00
einige Sekunden nach der zukünftigen Analyse verschoben sein.
Die APIs von JDK haben sich mit den aktuellen Anforderungen weiterentwickelt
- Die frühen Java-Versionen hatten genau das,
java.util.Date
was ein bisschen naiv war, vorausgesetzt, es gibt nur das Jahr, den Monat, den Tag und die Uhrzeit. Dies reichte schnell nicht aus.
- Auch die Anforderungen der Datenbanken waren unterschiedlich, so dass ziemlich früh
java.sql.Date
eingeführt wurde, mit seinen eigenen Einschränkungen.
- Da beide nicht unterschiedliche Kalender und Zeitzonen gut abdeckten, wurde die
Calendar
API eingeführt.
- Dies deckte die Komplexität der Zeitzonen immer noch nicht ab. Und doch war die Mischung der oben genannten APIs wirklich ein Problem. Als Java-Entwickler anfingen, an globalen Webanwendungen zu arbeiten, wurden Bibliotheken, die auf die meisten Anwendungsfälle abzielten, wie JodaTime, schnell populär. JodaTime war etwa ein Jahrzehnt lang der De-facto-Standard.
- Das JDK wurde jedoch nicht in JodaTime integriert, sodass die Arbeit damit etwas umständlich war. Also, nach einer sehr langen Diskussion darüber , wie die Sache zu nähern, JSR-310 wurde geschaffen , hauptsächlich basierend auf JodaTime .
Wie man damit in Java umgeht java.time
Bestimmen Sie, auf welchen Typ ein Zeitstempel analysiert werden soll
Wenn Sie eine Zeitstempelzeichenfolge verwenden, müssen Sie wissen, welche Informationen darin enthalten sind. Dies ist der entscheidende Punkt. Wenn Sie dies nicht richtig verstehen, erhalten Sie kryptische Ausnahmen wie "Instant kann nicht erstellt werden" oder "Zonenversatz fehlt" oder "Unbekannte Zonen-ID" usw.
Enthält es das Datum und die Uhrzeit?
Hat es einen Zeitversatz?
Ein Zeitversatz ist das +hh:mm
Teil. Manchmal +00:00
kann es Z
als "Zulu-Zeit", UTC
als koordinierte Weltzeit oder GMT
als mittlere Greenwich-Zeit ersetzt werden. Diese legen auch die Zeitzone fest.
Für diese Zeitstempel verwenden Sie OffsetDateTime
.
Hat es eine Zeitzone?
Für diese Zeitstempel verwenden Sie ZonedDateTime
.
Die Zone wird entweder durch angegeben
- Name ("Prag", "Pacific Standard Time", "PST") oder
- "zone ID" ("America / Los_Angeles", "Europe / London"), dargestellt durch java.time.ZoneId .
Die Liste der Zeitzonen wird von einer "TZ-Datenbank" zusammengestellt , die von ICAAN unterstützt wird.
Laut ZoneId
javadoc können die Zonen-IDs auch irgendwie als Z
und versetzt angegeben werden. Ich bin mir nicht sicher, wie dies realen Zonen zugeordnet wird. Wenn der Zeitstempel, der nur eine TZ hat, in eine Schaltstunde der Zeitversatzänderung fällt, ist er mehrdeutig und die Interpretation ist Gegenstand von ResolverStyle
(siehe unten).
Wenn dies nicht der Fall ist , wird der fehlende Kontext angenommen oder vernachlässigt. Und der Verbraucher muss sich entscheiden. Es muss also analysiert LocalDateTime
und konvertiert werden, OffsetDateTime
indem die fehlenden Informationen hinzugefügt werden:
- Sie können davon ausgehen, dass es sich um eine UTC-Zeit handelt. Fügen Sie den UTC-Offset von 0 Stunden hinzu.
- Sie können davon ausgehen, dass dies eine Zeit des Ortes ist, an dem die Konvertierung stattfindet. Konvertieren Sie es, indem Sie die Zeitzone des Systems hinzufügen.
- Sie können es vernachlässigen und einfach so verwenden, wie es ist. Dies ist nützlich, z. B. um zweimal zu vergleichen oder zu subtrahieren (siehe
Duration
) oder wenn Sie es nicht wissen und es nicht wirklich wichtig ist (z. B. lokaler Busfahrplan).
Teilzeitinformationen
- Nach dem , was der Zeitstempel enthält, können Sie nehmen
LocalDate
, LocalTime
, OffsetTime
, MonthDay
, Year
, oder YearMonth
aus ihm heraus.
Wenn Sie die vollständigen Informationen haben, können Sie eine erhalten java.time.Instant
. Dies wird auch intern zum Konvertieren zwischen OffsetDateTime
und verwendet ZonedDateTime
.
Finden Sie heraus, wie Sie es analysieren können
Es gibt eine umfangreiche Dokumentation, in DateTimeFormatter
der sowohl eine Zeitstempelzeichenfolge analysiert als auch eine Zeichenfolge formatiert werden kann.
Die vorgefertigten DateTimeFormatter
s sollten mehr als alle Standard-Zeitstempelformate abdecken. ISO_INSTANT
Kann zum Beispiel analysieren 2011-12-03T10:15:30.123457Z
.
Wenn Sie ein spezielles Format haben, können Sie Ihren eigenen DateTimeFormatter (der auch ein Parser ist) erstellen .
private static final DateTimeFormatter TIMESTAMP_PARSER = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.append(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SX"))
.toFormatter();
Ich empfehle, sich den Quellcode von anzuschauen DateTimeFormatter
und sich inspirieren zu lassen, wie man einen mit erstellt DateTimeFormatterBuilder
. ResolverStyle
Sehen Sie sich während Ihres Aufenthalts auch an, welche Steuerelemente für die Formate und mehrdeutigen Informationen LENIENT, SMART oder STRICT sind.
TemporalAccessor
Der häufigste Fehler besteht nun darin, auf die Komplexität von einzugehen TemporalAccessor
. Dies kommt davon, wie die Entwickler verwendet wurden, um damit zu arbeiten SimpleDateFormatter.parse(String)
. Richtig, DateTimeFormatter.parse("...")
gibt dir TemporalAccessor
.
// No need for this!
TemporalAccessor ta = TIMESTAMP_PARSER.parse("2011-... etc");
Ausgestattet mit den Kenntnissen aus dem vorherigen Abschnitt können Sie jedoch bequem den Typ analysieren, den Sie benötigen:
OffsetDateTime myTimestamp = OffsetDateTime.parse("2011-12-03T10:15:30.123457Z", TIMESTAMP_PARSER);
Das brauchst du eigentlich auch DateTimeFormatter
nicht. Die Typen, die Sie analysieren möchten, haben die parse(String)
Methoden.
OffsetDateTime myTimestamp = OffsetDateTime.parse("2011-12-03T10:15:30.123457Z");
In Bezug auf TemporalAccessor
, können Sie es verwenden , wenn Sie eine vage Vorstellung davon haben , welche Informationen es in der Zeichenfolge ist, und wollen zur Laufzeit entscheiden.
Ich hoffe, ich habe etwas Licht des Verstehens auf deine Seele geworfen :)
Hinweis: Es gibt einen Backport von java.time
Java 6 und 7: ThreeTen-Backport . Für Android hat es ThreeTenABP .
[1] Nicht nur, dass es sich nicht um Streifen handelt, sondern auch um einige seltsame Extreme. Beispielsweise haben einige benachbarte Pazifikinseln Zeitzonen von +14: 00 und -11: 00. Das heißt, während es auf einer Insel den 1. Mai um 15 Uhr gibt, ist es auf einer anderen Insel noch nicht der 30. April um 12 Uhr (wenn ich richtig gezählt habe :))
ZonedDateTime
eher eine als eine wollenLocalDateTime
. Der Name ist nicht intuitiv; dasLocal
bedeutet , jede Ortschaft im Allgemeinen eher als eine bestimmte Zeitzone. Als solches ist einLocalDateTime
Objekt nicht an die Zeitlinie gebunden. Um eine Bedeutung zu haben und einen bestimmten Moment in der Zeitleiste zu erhalten, müssen Sie eine Zeitzone anwenden.