it-swarm.com.de

Wie kann ich Datumsangaben mit LocalDateTime analysieren / formatieren? (Java 8)

Java 8 hat eine neue Java.time -API für die Arbeit mit Datums- und Uhrzeitangaben ( JSR 31 hinzugefügt ).

Ich habe Datum und Uhrzeit als Zeichenfolge (z. B. "2014-04-08 12:30"). Wie kann ich eine Instanz LocalDateTime aus der angegebenen Zeichenfolge erhalten?

Nachdem ich mit dem Objekt LocalDateTime fertig bin: Wie kann ich dann die Instanz LocalDateTime zurück in eine Zeichenfolge mit demselben Format wie oben konvertieren?

299
micha

Datum und Uhrzeit analysieren

Um ein Objekt LocalDateTime aus einer Zeichenfolge zu erstellen, können Sie die Methode static LocalDateTime.parse() verwenden. Es wird ein String und ein DateTimeFormatter als Parameter verwendet. Der DateTimeFormatter wird verwendet, um das Datums-/Zeitmuster anzugeben.

String str = "1986-04-08 12:30";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
LocalDateTime dateTime = LocalDateTime.parse(str, formatter);

Datum und Uhrzeit der Formatierung

Um eine formatierte Zeichenfolge aus einem LocalDateTime-Objekt zu erstellen, können Sie die Methode format() verwenden.

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
LocalDateTime dateTime = LocalDateTime.of(1986, Month.APRIL, 8, 12, 30);
String formattedDateTime = dateTime.format(formatter); // "1986-04-08 12:30"

Beachten Sie, dass in DateTimeFormatter einige häufig verwendete Datums-/Uhrzeitformate als Konstanten vordefiniert sind. Beispiel: Wenn Sie DateTimeFormatter.ISO_DATE_TIME zum Formatieren der Instanz LocalDateTime von oben verwenden, wird die Zeichenfolge "1986-04-08T12:30:00" angezeigt.

Die Methoden parse() und format() sind für alle datums- und zeitbezogenen Objekte verfügbar (z. B. LocalDate oder ZonedDateTime).

482
micha

Sie können LocalDate.parse() oder LocalDateTime.parse() auch für ein String verwenden, ohne es mit einem Muster zu versehen, wenn das String im ISO-8601-Format vorliegt.

zum Beispiel,

String strDate = "2015-08-04";
LocalDate aLD = LocalDate.parse(strDate);
System.out.println("Date: " + aLD);

String strDatewithTime = "2015-08-04T10:11:30";
LocalDateTime aLDT = LocalDateTime.parse(strDatewithTime);
System.out.println("Date with Time: " + aLDT);

Ausgabe ,

Date: 2015-08-04
Date with Time: 2015-08-04T10:11:30

und verwenden Sie DateTimeFormatter nur, wenn Sie sich mit anderen Datumsmustern befassen müssen, z. B. dd MMM uuuu steht für den Tag des Monats (zweistellig), drei Buchstaben des Monatsnamens (Jan, Feb, Mar, ...) und ein vierstelliges Jahr:

DateTimeFormatter dTF = DateTimeFormatter.ofPattern("dd MMM uuuu");
String anotherDate = "04 Aug 2015";
LocalDate lds = LocalDate.parse(anotherDate, dTF);
System.out.println(anotherDate + " parses to " + lds);

Ausgabe

04 Aug 2015 parses to 2015-08-04

denken Sie auch daran, dass das Objekt DateTimeFormatter bidirektional ist. Es kann sowohl Eingaben analysieren als auch Ausgaben formatieren.

String strDate = "2015-08-04";
LocalDate aLD = LocalDate.parse(strDate);
DateTimeFormatter dTF = DateTimeFormatter.ofPattern("dd MMM uuuu");
System.out.println(aLD + " formats as " + dTF.format(aLD));

Ausgabe

2015-08-04 formats as 04 Aug 2015

(Siehe vollständiges Liste der Muster zum Formatieren und Analysieren von DateFormatter )

  Symbol  Meaning                     Presentation      Examples
  ------  -------                     ------------      -------
   G       era                         text              AD; Anno Domini; A
   u       year                        year              2004; 04
   y       year-of-era                 year              2004; 04
   D       day-of-year                 number            189
   M/L     month-of-year               number/text       7; 07; Jul; July; J
   d       day-of-month                number            10

   Q/q     quarter-of-year             number/text       3; 03; Q3; 3rd quarter
   Y       week-based-year             year              1996; 96
   w       week-of-week-based-year     number            27
   W       week-of-month               number            4
   E       day-of-week                 text              Tue; Tuesday; T
   e/c     localized day-of-week       number/text       2; 02; Tue; Tuesday; T
   F       week-of-month               number            3

   a       am-pm-of-day                text              PM
   h       clock-hour-of-am-pm (1-12)  number            12
   K       hour-of-am-pm (0-11)        number            0
   k       clock-hour-of-am-pm (1-24)  number            0

   H       hour-of-day (0-23)          number            0
   m       minute-of-hour              number            30
   s       second-of-minute            number            55
   S       fraction-of-second          fraction          978
   A       milli-of-day                number            1234
   n       nano-of-second              number            987654321
   N       nano-of-day                 number            1234000000

   V       time-zone ID                zone-id           America/Los_Angeles; Z; -08:30
   z       time-zone name              zone-name         Pacific Standard Time; PST
   O       localized zone-offset       offset-O          GMT+8; GMT+08:00; UTC-08:00;
   X       zone-offset 'Z' for zero    offset-X          Z; -08; -0830; -08:30; -083015; -08:30:15;
   x       zone-offset                 offset-x          +0000; -08; -0830; -08:30; -083015; -08:30:15;
   Z       zone-offset                 offset-Z          +0000; -0800; -08:00;

   p       pad next                    pad modifier      1

   '       escape for text             delimiter
   ''      single quote                literal           '
   [       optional section start
   ]       optional section end
   #       reserved for future use
   {       reserved for future use
   }       reserved for future use
134
Sufiyan Ghori

Beide Antworten oben erklären die Frage nach den Zeichenfolgenmustern sehr gut. Nur für den Fall, dass Sie mit ISO 8601 arbeiten, müssen Sie DateTimeFormatter nicht anwenden, da LocalDateTime bereits darauf vorbereitet ist:

LocalDateTime in Zeitzone ISO8601 konvertieren String

LocalDateTime ldt = LocalDateTime.now(); 
ZonedDateTime zdt = ldt.atZone(ZoneOffset.UTC); //you might use a different zone
String iso8601 = zdt.toString();

Von ISO8601-Zeichenfolge zurück in LocalDateTime konvertieren

String iso8601 = "2016-02-14T18:32:04.150Z";
ZonedDateTime zdt = ZonedDateTime.parse(iso8601);
LocalDateTime ldt = zdt.toLocalDateTime();
32
Marcio Jasinski

Das Parsen eines Strings mit Datum und Uhrzeit zu einem bestimmten Zeitpunkt (von Java als " Instant " bezeichnet) ist recht kompliziert. Java hat dies in mehreren Iterationen angegangen. Die neueste Version, _Java.time_ und _Java.time.chrono_, deckt fast alle Anforderungen ab (außer Time Dilation :)).

Diese Komplexität bringt jedoch viel Verwirrung mit sich.

Der Schlüssel zum Verständnis der Datumsanalyse ist:

Warum hat Java so viele Möglichkeiten, ein Datum zu analysieren?

  1. Es gibt mehrere Systeme, um eine Zeit zu messen. Beispielsweise wurden die historischen japanischen Kalender aus den Zeiträumen der Regierungszeit des jeweiligen Kaisers oder der jeweiligen Dynastie abgeleitet. Dann gibt es z.B. UNIX-Zeitstempel. Glücklicherweise hat es die ganze (Geschäfts-) Welt geschafft, dasselbe zu nutzen.
  2. In der Vergangenheit wurden die Systeme aus verschiedenen Gründen von/auf umgestellt. Z.B. vom julianischen Kalender zum gregorianischen Kalender im Jahre 1582. Deshalb müssen 'westliche' Daten davor anders behandelt werden.
  3. Und natürlich ist die Änderung nicht sofort eingetreten. Da der Kalender aus dem Hauptquartier einiger religiöser und anderer Teile Europas stammte, die an andere Religionsgemeinschaften glaubten, wechselte beispielsweise Deutschland erst um das Jahr 1700.

... und warum ist die LocalDateTime, ZonedDateTime et al. so kompliziert

  1. Es gibt Zeitzonen . Eine Zeitzone ist im Grunde genommen ein "Streifen" * [1] der Erdoberfläche, dessen Autoritäten denselben Regeln folgen: Wann hat sie welchen Zeitversatz? Dies schließt Sommerzeitregeln ein.
    Die Zeitzonen ändern sich im Laufe der Zeit für verschiedene Gebiete, hauptsächlich basierend darauf, wer wen erobert. Und die Regeln einer Zeitzone Änderung im Laufe der Zeit ​​ebenfalls.

  2. Es gibt Zeitverschiebungen. Dies ist nicht dasselbe wie Zeitzonen, da eine Zeitzone z.B. "Prag", 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 liegt. Während der Schaltstunde kann der Zeitstempel zwei verschiedene Zeiten bedeuten, also ohne zusätzliche Informationen kann nicht zuverlässig konvertiert werden.
    Hinweis: Mit timestamp meine ich "eine Zeichenfolge, die ein Datum und/oder eine Uhrzeit enthält, optional mit einer Zeitzone und/oder einem Zeitversatz".

  3. Für bestimmte Zeiträume können sich mehrere Zeitzonen den gleichen Zeitversatz teilen. Beispielsweise entspricht die GMT/UTC-Zeitzone der "London" -Zeitzone, wenn der Sommerzeitversatz nicht wirksam ist.

Um es etwas komplizierter zu machen (aber das ist für Ihren Anwendungsfall nicht so wichtig):

  1. Die Wissenschaftler beobachten die Dynamik der Erde, die sich mit der Zeit ändert. darauf aufbauend addieren sie am ende einzelner jahre sekunden. (Also kann _2040-12-31 24:00:00_ eine gültige Datums-/Uhrzeitangabe sein.) Hierfür sind regelmäßige Aktualisierungen der Metadaten erforderlich, die Systeme verwenden, um die Datumskonvertierung zu gewährleisten. Z.B. Unter Linux erhalten Sie regelmäßige Updates für die Pakete Java, einschließlich dieser neuen Daten.
  2. 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 eine Zeitzonenänderung herum zu einem Vergleich führt kann zu unterschiedlichen Ergebnissen führen , wenn verschiedene Versionen der Software ausgeführt werden. Dies gilt auch für den Vergleich zwischen der betroffenen Zeitzone und einer anderen Zeitzone.

    Sollte dies einen Fehler in Ihrer Software verursachen, ziehen Sie in Betracht, einen Zeitstempel zu verwenden, der keine so komplizierten Regeln enthält, wie NIX-Zeitstempel .

  3. Aufgrund von 7 können wir Daten für zukünftige Daten nicht genau mit Sicherheit konvertieren. So kann beispielsweise das aktuelle Parsen von _8524-02-17 12:00:00_ ein paar Sekunden vor dem zukünftigen Parsen liegen.

Die APIs von JDK hierfür haben sich mit den aktuellen Anforderungen entwickelt

  • Die frühen Versionen von Java hatten nur _Java.util.Date_, was ein wenig naiv war, vorausgesetzt, es gibt nur das Jahr, den Monat, den Tag und die Uhrzeit. Das hat schnell nicht gereicht.
  • Außerdem waren die Anforderungen der Datenbanken unterschiedlich, so dass _Java.sql.Date_ mit seinen eigenen Einschränkungen schon sehr früh eingeführt wurde.
  • Da keiner der beiden Kalender und Zeitzonen gut abdeckte, wurde die API Calendar 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 für die Arbeit. Als Java Entwickler mit der Arbeit an globalen Webanwendungen begannen, wurden Bibliotheken, die auf die meisten Anwendungsfälle abzielten, wie JodaTime, schnell populär. JodaTime war ungefähr ein Jahrzehnt lang der De-facto-Standard.
  • Da sich das JDK jedoch nicht in JodaTime integrieren ließ, war die Arbeit damit etwas umständlich. Nach einer sehr langen Diskussion über die Vorgehensweise wurde JSR-31 erstellt hauptsächlich basierend auf JodaTime .

Wie gehe ich damit um in Javas _Java.time_

Bestimmen Sie, auf welchen Typ ein Zeitstempel analysiert werden soll

Wenn Sie eine Timestamp-Zeichenfolge verwenden, müssen Sie wissen, welche Informationen darin enthalten sind. Dies ist der entscheidende Punkt. Wenn Sie dies nicht richtig verstehen, kommt es zu kryptischen Ausnahmebedingungen wie "Instant kann nicht erstellt werden" oder " Zonenoffset fehlt "oder" unbekannte Zonen-ID "usw.

Enthält es das Datum und die Uhrzeit?

  1. Hat es einen Zeitversatz?
    Ein Zeitversatz ist der Teil _+hh:mm_. Manchmal kann _+00:00_ durch Z als 'Zulu time', UTC als Universal Time Coordinated oder GMT als Greenwich Mean Time ersetzt werden. Diese stellen auch die Zeitzone ein.
    Für diese Zeitstempel verwenden Sie OffsetDateTime .

  2. Hat es eine Zeitzone?
    Für diese Zeitstempel verwenden Sie ZonedDateTime .
    Zone wird entweder durch angegeben

    • name ("Prag", "Pacific Standard Time", "PST") oder
    • "Zonen-ID" ("America/Los_Angeles", "Europe/London"), dargestellt durch Java.time.ZoneId .

    Die Liste der Zeitzonen wird von einer "TZ-Datenbank" erstellt, die von ICAAN unterstützt wird.

    Gemäß javadoc von ZoneId können die Zonen-IDs auch irgendwie als Z und offset angegeben werden. Ich bin mir nicht sicher, wie das auf reale Zonen abgebildet 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.

  3. Wenn es keine hat, wird der fehlende Kontext angenommen oder vernachlässigt. Und der Verbraucher muss entscheiden. Daher muss es als LocalDateTime analysiert und durch Hinzufügen der fehlenden Informationen in OffsetDateTime konvertiert werden:

    • Sie können annehmen , dass es sich um eine UTC-Zeit handelt. Fügen Sie den UTC-Offset von 0 Stunden hinzu.
    • Sie können annehmen , dass es eine Zeit des Ortes ist, an dem die Konvertierung stattfindet. Konvertieren Sie es, indem Sie die Zeitzone des Systems hinzufügen.
    • Sie können vernachlässigen und es einfach so verwenden, wie es ist. Das ist nützlich, z.B. zwei Mal zu vergleichen oder zu subtrahieren (siehe Duration ) oder wenn Sie es nicht wissen und es nicht wirklich wichtig ist (z. B. lokaler Busfahrplan).

Teilzeitinformation

  • Je nachdem, was der Zeitstempel enthält, können Sie LocalDate, LocalTime, OffsetTime, MonthDay, Year oder YearMonth herausnehmen.

Wenn Sie die vollständigen Informationen haben, erhalten Sie einen Java.time.Instant . Dies wird auch intern zum Konvertieren zwischen OffsetDateTime und ZonedDateTime verwendet.

Finde heraus, wie du es analysieren kannst

Es gibt eine umfangreiche Dokumentation zu DateTimeFormatter , mit der sowohl eine Zeitstempelzeichenfolge als auch ein Format für eine Zeichenfolge analysiert werden kann.

Das vorgefertigte DateTimeFormatters sollte mehr als alle Standard-Zeitstempelformate abdecken. Zum Beispiel kann _ISO_INSTANT2011-12-03T10:15:30.123457Z_ analysieren.

Wenn Sie ein spezielles Format haben, können Sie Ihren eigenen DateTimeFormatter erstellen (der auch ein Parser ist).

_private static final DateTimeFormatter TIMESTAMP_PARSER = new DateTimeFormatterBuilder()
   .parseCaseInsensitive()
   .append(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SX"))
   .toFormatter();
_

Ich empfehle, den Quellcode von DateTimeFormatter zu lesen und mich davon inspirieren zu lassen, wie man einen mit DateTimeFormatterBuilder erstellt. Schauen Sie sich dort auch ResolverStyle an, das steuert, ob der Parser für die Formate und mehrdeutigen Informationen LENIENT, SMART oder STRICT ist.

TemporalAccessor

Nun ist der häufige Fehler, auf die Komplexität von TemporalAccessor einzugehen. Dies ist darauf zurückzuführen, wie die Entwickler mit SimpleDateFormatter.parse(String) gearbeitet haben. Richtig, DateTimeFormatter.parse("...") gibt Ihnen TemporalAccessor.

_// No need for this!
TemporalAccessor ta = TIMESTAMP_PARSER.parse("2011-... etc");
_

Ausgestattet mit den Kenntnissen aus dem vorherigen Abschnitt können Sie jedoch ganz bequem den Typ eingeben, den Sie benötigen:

_OffsetDateTime myTimestamp = OffsetDateTime.parse("2011-12-03T10:15:30.123457Z", TIMESTAMP_PARSER);
_

Du brauchst eigentlich auch nicht die DateTimeFormatter. Die Typen, die Sie analysieren möchten, haben die Methoden parse(String).

_OffsetDateTime myTimestamp = OffsetDateTime.parse("2011-12-03T10:15:30.123457Z");
_

Was TemporalAccessor betrifft, können Sie es verwenden, wenn Sie eine vage Vorstellung davon haben, welche Informationen in der Zeichenfolge enthalten sind und zur Laufzeit entscheiden möchten.

Ich hoffe, ich habe ein bisschen Licht auf deine Seele geworfen :)

Hinweis: Es gibt einen Backport von _Java.time_ zu 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 seltsame Extreme. Zum Beispiel haben einige benachbarte Pazifikinseln +14: 00 und -11: 00 Zeitzonen. Das bedeutet, dass auf einer Insel der 1. Mai um 15 Uhr ist, auf einer anderen Insel ist es noch der 30. April. 12 PM (wenn ich richtig gezählt habe :))

4
Ondra Žižka