create mobile friendly website
Java8 die zeiten aendern sich

Java 8 - Die neue Version (Teil IV)

Die Zeiten ändern sich

Endlich ändern sich die Zeiten, werden sicher viele sagen. Mit Java 8 hat auch die neue Date/Time-API Einzug in den Standard gehalten. Jeder der schon einmal das „Vergnügen“ hatte, mit den vorherigen Java-Versionen eine komplexere Datumsarithmetik zu entwickeln, wird schnell an die Grenzen bzw. an deren Eigenheiten gestoßen sein, die augenscheinlich nicht mehr in die moderne Java-Welt passen.  Im folgenden Artikel, wollen wir die neue Date/Time-API einmal genauer betrachten und beleuchten, wie sie funktioniert und wie wir ihre Vorteile nutzen können.

Warum ist eine neue API überhaupt notwendig?

Schnell kommt die Frage auf, warum wird überhaupt eine neue API benötigt? Die Antwort liegt auf der Hand, wenn man sich einmal die Nachteile der alten API vor Augen führt:

Fast jeder wird schon einmal bei dem Objekt Calendar über die Zählweise der Monate gestolpert sein. Es wurde wohl das alte Konzept zugrunde gelegt, dass es in der Entwicklung üblich ist, bei Iterationen mit 0 zu beginnen. An dieser Regel wurde auch bei den Monaten festgehalten.

Daher ist der Dezember „verständlicher Weise“ der Monat „11“ und der Januar dementsprechend der Monat „0“ eines Jahres. Über diese Logik könnte ggf. hinwegsehen werden, wenn man weiß, womit man es zu tun hat und mindestens einmal darauf hereingefallen ist.

Viel gravierender sind hingegen die Nachteile der Thread-Sicherheit. Das Objekt Date ist „mutable“ und ist demnach nicht Thread-sicher. Alle Zugriffe innerhalb von Threads müssten daher synchronisiert werden.

In Wirklichkeit ist das Objekt Date auch kein Datum, sondern ein Zeitstempel. Diesen kennen wir aus der Unix-Welt und startet entsprechend am 01. Januar 1970. Die Genauig­keit dieses Zeitstempels ist auf Millisekunden beschränkt. Eine Berechnung von beispielsweise Nano­sekunden ist daher leider nicht möglich.

Es gibt noch eine Reihe weiterer Nachteile, die hier aufge­listet werden könnten, z.B. dass das Objekt Date keine Zeitzonen und auch keine Sommer-/Winterzeit kennt. Wir wollen uns jedoch nicht nur mit den Nachteilen der alten Welt befassen, sondern uns lieber den Neuerungen widmen.

Technischer Überblick

Die neue Date/Time-API wurde in Form des JSR 310 spezifiziert. Auch hier wurde das Rad nicht neu erfunden, sondern ein bewährtes Framework als Vorlage genommen. Große Teile der neuen API basieren auf der Bibliothek Joda-Time, welche bereits sehr früh als Referenz galt, wenn es in Java-Anwendungen um komplexe Datums­arithmetik ging. Sowohl die Bibliothek Joda-Time, als auch die neue Date/Time-API basieren auf der Designstudie „TimeAndMoney“ von Eric Evans (siehe Quelle [1]).

Alle Neuerungen der Date/Time-API befinden sich in den folgenden Packages:

  • java.time
    Klassen für Datum, Uhrzeit, Datum und Uhrzeit kombiniert, Zeitzonen, Zeitpunkte, Dauer und Uhren
  • java.time.chrono
    API für Kalendersysteme, die nicht dem Standard ISO 8601 entsprechen
  • java.time.format
    Klassen zum Parsen und Formatieren von Daten und Uhrzeiten
  • java.time.temporal
    Erweiterte API, die die Zusammenarbeit zwischen Datums- und Zeitklassen, Abfragen und Anpassungen an Daten und Uhrzeiten ermöglicht
  • java.time.zone
    Klassen, die Zeitzonen, Differenzen zwischen Zeitzonen und Regeln der jeweiligen Zeitzonen unterstützen


Im Folgenden wollen wir uns ein paar ausgesuchte Aspekte der neuen API genauer anschauen. Natürlich ist es nicht möglich in diesem Artikel jedes Detail der neuen API aufzuzeigen, dennoch sollen Sie ein Gefühl dafür bekommen, wie die neue API einzusetzen und wie deren Mehrwert einzuschätzen ist.

java.util.Date vs. java.time.Instant / Zeitpunkte

Das klassische java.util.Date ist genau genommen kein Datum, auch wenn der Name es vielleicht suggeriert. Es ist vielmehr ein definierter Zeitpunkt, der in Milli­sekunden ausgedrückt ist. In der neuen API gibt es hierfür die Klasse java.time.Instant. Auch sie repräsentiert einen bestimmten Zeitpunkt - mit dem Unterschied, dass die davon erzeugten Objekte unveränderlich und damit Thread-sicher sind und die Genauigkeit bis auf die Nanosekunde geht.

Ein Objekt von Typ java.util.Date kann sehr einfach in ein Objekt vom Typ java.time.Instant konvertiert werden. Dafür wurde die alte Klasse java.util.Date um die Methode getInstant erweitert. Wie der Name schon verrät, wird hier ein neues Instant-Objekt auf Basis des Date-Objektes erzeugt und zurückgegeben. Wahlweise kann hierfür auch die Methode ofEpochMilli von der Klasse Instant verwendet werden. Hier muss lediglich der Wert übergeben werden, der von der Methode getTime des Date-Objektes zurückgeliefert wird. Für den umgekehrten Weg, wurde die Klasse java.util.date um die Methode from erweitert.

Die Verwendung der Klasse wird in der Abbildung 1 verdeutlicht. In dem Code-Beispiel wird die einfache Verwendung und die Konvertierung der jeweiligen Klassen aufgezeigt.

Daten und Uhrzeiten

Wird mehr als ein Zeitpunkt (Instant) benötigt, griff man bisher immer auf das Objekt Calender zurück. Auf ein paar Nachteile dieser Klasse wurde bereits einleitend einge­gangen. Bei der neuen Date/Time-API gibt es für die Datums­arithmetik nun eine Vielzahl von Klassen, die nur die Menge an Informationen beinhalten, welche auch wirklich benötigt werden. Hierzu zählen unter anderem die folgenden Klassen:

  • LocalDate
    Datum ohne Zeitangabe
  • LocalTime
    Uhrzeit ohne Datumsinformationen
  • LocalDateTime
    Datum inklusive Uhrzeit

Es wird deutlich, dass für jedes Szenario eine eigene Klasse existiert. Das macht den Code in jedem Fall klarer und dadurch auch besser wartbar. Zuvor konnte anhand des Objektes nicht unterschieden werden, ob beispiels­weise die Uhrzeit eine Rolle spielt oder nicht.

Anstelle der bekannten Methoden add und set für die Datenmanipulation bei der Klasse java.util.Calender, stehen nun die Methoden plus, minus und with zur Verfügung. Die Methoden plus und minus sind, wie der Name vermuten lässt, für die Addition bzw. Subtraktion von Daten und Uhrzeiten zuständig. Mit der Methode with werden einzelne Eigenschaften bzw. Informationen des Datums oder der Uhrzeit neu gesetzt. Zum Beispiel kann mit dem Aufruf withDayOfMonth(1) das zugehörige LocalDate- oder LocalDateTime-Objekt auf den ersten Tag des jeweiligen Monats gesetzt werden (siehe Abbildung 2).

Diese Methoden lassen sich beliebig kaskadieren. Das Konzept ist auch unter dem Begriff „Fluent Interfaces“ bekannt und wurde von Martin Fowler und Eric Evans entwickelt (siehe Quelle [2]).

Zeitzonen

Bei der Datumsarithmetik gibt es kaum eine schwierigere Aufgabe, als die Berechnung von Daten über verschiedene Zeitzonen hinweg. Für diesen Anwendungsfall wurde die alte API so gut wie nie verwendet und das hat auch seinen guten Grund.

Die neue Date/Time-API versucht sich dem Problem anzunehmen und stellt für diesen Anwendungsfall u.a. drei weitere Klassen zur Verfügung:

  • ZoneId
    Diese Klasse definert eine Zeitzone als eindeutige ID und beinhaltet Regeln für die Konvertierung zwischen den Objekten Instant und LocalDateTime.
  • ZoneOffset
    Die Klasse beinhaltet die Abweichung von einer Zeitzone zu der Zeitzone „Greenwich/UTC“.
  • ZonedDateTime
    DateTime-Objekt mit zusätzlicher Information der zugehörigen Zeitzone

In der Abbildung 3 wird die örtliche Ankunftszeit eines Fluges von Berlin nach Tokio berechnet. Für eine Berechnung über die jeweiligen Zeitzonen hinweg wird zunächst das Abflugdatum in Form eines Objektes ZoneDateTime benötigt. Um dieses Objekt zu erzeugen wird von der Klasse ZoneDateTime die Methode of aufgerufen. Dieser wird anschließend das zugehörige Datum in Form eines LocalDateTime-Objektes und die zugehörige Zeitzone in Form eines Objektes von Typ ZoneId übergeben. Im Anschluss daran, wird auf dem neuen Objekt mit der Methode withZoneSameInstant die Zeitzone von Tokio übergeben. Die Methode gibt eine Kopie des Abflug­datums zurück, jedoch auf Basis der Zeitzone von Tokio. Nun muss lediglich die Flugdauer mit der Methode plusMinutes addiert werden und man erhält das korrekte Ankunftsdatum in Tokio.

Dieses simple Beispiel zeigt sehr anschaulich, wie einfach und klar sich Berechnungen über verschiedene Zeitzonen hinweg mit der neuen Date/Time-API realisieren lassen.

Datumsarithmetik

Für Berechnungen, welche über die einfache Addition und Subtraktion von Daten und Uhrzeiten hinausgehen, wird das Package java.time.temporal zur Verfügung gestellt. Es bietet viele verschiedene Möglichkeiten komplexere Berechnungen auf Zeiten und Daten auszuführen. Das Package liefert darüber hinaus weitere Schnittstellen, mit denen man eigene Data-Time-Klassen oder eigene Kalendersysteme implementieren kann.

Für die meisten Berechnungen kann der Enum-Datentyp ChronoUnit herangezogen werden. Sie besitzt viele verschiedene Konstanten für verschiedene Anwendungsfälle. Neben den üblichen Konstanten für Tage, Monate, Jahre, Stunden und Sekunden, werden auch Konstanten für spezielle Anwendungsfälle angeboten, wie z.B. Ära, Dekaden und Jahrhunderte. Diese Konstanten besitzen alle eine Vielzahl von Methoden, die sich gut für die Datums­arithmetik eignen. Stellt sich z.B. die Frage, wie lange ein Flug dauert, wenn der Flug um 02:00 Uhr in Berlin startet und am darauf folgenden Tag um 10:00 Uhr Ortszeit in Sydney landet, kann das mit der neuen Date/Time-API sehr einfach berechnet werden.

Dafür werden, wie im vorherigen Beispiel, einfach die passenden Objekte vom Typ ZoneDateTime erzeugt und diese Objekte anschließend mit der Methode between der Konstante MINUTES übergeben. Das long-Ergebnis repräsentiert die Minuten, die der Flug tatsächlich ge­dauert hat (siehe Abbildung 4). Das ist ein sehr einfaches Beispiel, dennoch wird deutlich, dass sich auch komplexere Berechnungen mit der neuen API einfach und sauber abbilden lassen.

Parsen und Formatieren

Auf die bekannte Klasse SimpleDateFormat für das Parsen und Formatieren von Werten wird nun nicht mehr zurückgegriffen. Stattdessen wir die neue Klasse DateTimeFormatter verwendet.

Viele Klassen der Date/Time-API stellen dafür die Methoden parse und format zur Verfügung. Bei parse handelt es sich um eine statische Methode die dazu dient, ein Datum bzw. eine Uhrzeit in Form eines String in das jeweilige Objekt zu parsen. Neben diesem String-Parameter wird der Methode zusätzlich ein Objekt von Typ DateTimeFormatter übergeben, in dem das Format des zu parsenden Datums definiert ist (siehe Abbildung 5).

Die Methode format unterstützt den umgekehrten Weg. Es wird lediglich ein Objekt vom Typ DateTimeFormatter übergeben und ein String in dem übergebenen Format zurückgegeben (siehe Abbildung 6).

Beim DateTimeFormatter können sowohl eigene Pattern definiert werden, als auch bereits vorhandene genutzt werden.

Ist die Zeit reif?

Die Frage, ob die Zeit reif für eine neue Date/Time-API ist, kann nur mit einem klaren „Ja“ beantwortet werden. Der Ausdruck „überreif“ würde vielleicht noch ein wenig besser passen, denn leider ist die neue Date/Time-API erst Bestandteil von Java 8. Das Problem daran ist, dass viele Frameworks noch länger mit der Einbindung der API warten werden, da sie häufig auch noch mit älteren Java-Versionen laufen und entsprechend abwärtskompatibel sein müssen.

Daher werden wir leider noch länger mit der alten API arbeiten müssen und sie noch einige Jahre in bestehenden Projekten vorfinden. Deshalb hätte es sicher nicht ge­schadet, wenn die neue API bereits in älteren Java-Versionen Einzug gehalten hätte.

Damit wir aber in unserem selbstgeschriebenen Code bereits die neuen Objekte einsetzen können, wurden viele Schnittstellen und Methoden bereitgestellt, die den Wechsel von der „alten“ in die „neue“ Welt so einfach wie möglich machen sollen.

Fazit

Die neue Date/Time-API bietet viele Möglichkeiten, die sich zuvor nur schwer oder ausschließlich durch externe Bibliotheken, wie beispielsweise der Joda-API realisieren ließen.

Wir konnten in diesem Artikel nicht auf alle neuen Funktionalitäten eingehen. Dennoch ist deutlich geworden, wie die neue API funktioniert und welchen Mehrwert man bei der allgemeinen Datumsarithmetik durch sie erfährt. Sollten Sie noch weitergehende Fragen zu den Neuerungen in Java 8 haben, stehen wir Ihnen gerne zur Verfügung.

Christian Wiesing
()

 

ORDIX aktuell

Soziales Engagement – Die ORDIX AG unterstützt gemeinnützige Organisationen zu
Weihnachten
Die Unterstützung von karitativen Organisationen ist ein wesentlicher Bestandteil der ORDIX Philosophie. Es ist zur Trad...
Weiterlesen ...
ORDIX sucht Nachwuchs auf der hobit 2017
Mit der hobit 2017 hat die ORDIX AG vom 24. bis 26.01.2017 das erste Mal an der Darmstädter Ausbildungsmesse teilgenomme...
Weiterlesen ...
Lesestoff zum Weihnachtsfest: ORDIX®   news 2/2016
​Die aktuelle ORDIX® news 2/2016 hält zum Ende des Jahres wieder interessante Artikel rund um die IT bereit. Ob Big Data...
Weiterlesen ...

Kostenloses Kundenmagazin

Abbildungen

Die vollständigen Artikel mit allen Abbildungen finden Sie im blätterbaren PDF.
Eine Übersicht über alle Artikel sowie das PDF zum Download finden Sie im Inhaltsverzeichnis.

Impressum

Impressum der ORDIX® news 1/2015

Links

[1] Webseite der Java Community – Übersicht zu JSR 310
[2] API von Joda-Time
[3] Java Tutorial zur Date/Time-API von Oracle

Quellen

[1] Designstudie „TimeAndMoney“ von Eric Evans
[2] Blogeintrag von Martin Fowler zum Thema „Fluent Interfaces“

Bildnachweis

© flickr | Jannis_V | Pocket Watch
© flickr | MIKI Yoshihito| Display Casio