Der Sprung in die moderne Zeitrechnung mit java.time

Montag, 24.2.2025

Der Sprung in die moderne Zeitrechnung mit java.time,

Viele Java-Anwendungen nutzen noch die veralteten Klassen java.util.Date, java.sql.Date und java.util.Calendar, welche in Java 1.0 und 1.1 eingeführt wurden, um Datum- und Zeitinformationen darzustellen und zu verarbeiten. Diese APIs sind komplex und die fehlende Unterstützung für oft benötigte Operationen, beispielsweise die Berechnung der Zeit zwischen zwei Zeitpunkten, behindern nicht nur die Entwicklung, sondern sind auch häufige Fehlerursachen. In Java 8 wurde mit java.time eine neue API eingeführt, die diese Probleme lösen soll.  

In diesem Blog-Artikel möchte ich auf einige Probleme der alten APIs hinweisen und aufzeigen, was bei der Migration zu java.time zu beachten ist. 

Probleme von java.util.Date

Die Klasse java.util.Date ist seit den frühen Tagen von Java Teil der Standardbibliothek und dient der repräsentativen Handhabung von Zeitpunkten und Zeitangaben. Sie wurde entworfen, um sowohl Datums- als auch Zeitinformationen in einem einzigen Objekt zu speichern. Trotz ihrer weit verbreiteten Verwendung hat sich jedoch gezeigt, dass java.util.Date viele Einschränkungen und Probleme mit sich bringt, darunter Unsicherheiten bei der Behandlung von Zeitzonen, Mutabilität und eine unübersichtliche API. Diese Mängel haben in der modernen Softwareentwicklung den Bedarf an einer besser strukturierten und benutzerfreundlicheren Lösung, wie sie durch die java.time API bereitgestellt wird, verstärkt.

 

Problem 1 Veränderlichkeit

Instanzen von java.util.Date sind veränderlich. Während dies aus Sicht der Flexibilität nützlich erscheint, birgt es erhebliche Risiken in Multi-Threading-Umgebungen. Greifen mehrere Threads auf dieselbe Date-Instanz zu, können u.a. schwer diagnostizierbare Race Conditions die Folge sein. Um dies zu vermeiden, müssen Synchronisationsmechanismen eingeführt werden, was zusätzlichen Aufwand und eine erhöhte Komplexität zur Folge hat. 

 

In dem Code-Beispiel kann die Ausgabe variieren.

Instanzen von java.time-Klassen wie z.B. java.time.Instant hingegen sind unveränderlich. Änderungen an Objekten dieser Klassen erzeugen stattdessen neue Objekte, womit Thread-Sicherheit garantiert werden kann.  

 

 

Problem 2 Unübersichtliche API

Die Methoden von java.util.Date sind nicht ausreichend, um gängige Aufgaben intuitiv zu bearbeiten. Wie im vorherigen Beispiel gezeigt, erweist sich die Addition von 10 Sekunden zu einem bestehenden Zeitpunkt als umständlich, da man dafür 10.000 Millisekunden addieren muss. Zudem sind Methoden wie date.getSeconds() und date.setSeconds() bereits als deprecated markiert. 

Häufig ist es erforderlich, komplexe Logik (wie etwa How can I calculate a time span in Java and format the output? ) zu entwickeln, um grundlegende Anforderungen wie die Berechnung der Zeitspanne zwischen zwei Daten zu erfüllen. Dies führt zu unübersichtlichem Code und erschwert die Wartbarkeit der Anwendung. 

java.time hingegen beinhaltet Klassen wie Period und Duration für diesen Anwendungsfall. So können Zeitspannen zwischen 2 Zeitstempeln ohne viel Aufwand berechnet, umgerechnet und ausgegeben werden.

 

 

Problem 3 Zeitdarstellung und Zeitzonen 

java.util.Date speichert einen Unix-Zeitstempel ohne Zeitzoneninformation. Wenn die Methode toString() aufgerufen wird, wird das Datum in der lokalen Zeitzone des Systems ausgegeben. Dies kann dazu führen, dass unterschiedliche Systeme unterschiedliche Darstellungen der selben Instanz zurückliefern. Im Gegensatz dazu bieten die Klassen java.time.ZonedDateTime und java.time.OffSetDateTime eine explizite Handhabung von Zeitzonen.  Hierbei werden Zeit und Zeitzone in einem einzigen Objekt gespeichert, wodurch sie unabhängig von der Umgebung bleiben. Zusätzlich wird die Umwandlung zwischen Zeitzonen vereinfacht.  

 

 

Problem 4 java.sql.Date

Die Klasse java.sql.Date stellt eine Schnittstelle zu Datenbanken über JDBC dar und entspricht dem SQL DATE, das nur das Datum ohne Zeitinformationen speichert. Im Gegensatz dazu enthält java.util.Date auch Zeitinformationen, was zu potenziellen Informationsverlusten bei der naheliegenden Konvertierung zwischen diesen beiden Objekten führen kann. Um eine korrekte Konvertierung sicherzustellen, sollte stattdessen von java.util.Date zu java.sql.Timestamp gewechselt werden, da letzterer sowohl Datum als auch Zeit erfasst und somit Verlust von Informationen vermeidet. Diese fehleranfällige Typ-Konvertierung entfällt mit java.time, da java.time-Objekte ab JDBC Version 4.2 auch direkt genutzt werden können, um Datenbankzugriffe zu realisieren.

 

Migration 

Der erste Schritt der Migration besteht darin, alle Vorkommen von java.util.Date im Code zu identifizieren. Hierbei sollte auch auf Verwendungen von java.sql-Klassen wie java.sql.Date, java.sql.Timestamp etc. geachtet werden, da auch diese Klassen konvertiert werden können.  Bei diesem Schritt kann eine automatisierte Architekturprüfung mit ArchUnit unterstützen ( Automatisierte Architekturprüfungen mit ArchUnit – Praxiswissen ). Für die Migration zur neuen API wurde den alten Klassen eine Methode toInstant() zur Verfügung gestellt, die eine java.time.Instant Instanz aus Objekten der alten Klassen erzeugt. Aus diesem Objekt kann dann, je nach Anwendungsfall, ein Objekt der geeigneten Klasse der java.time API generiert werden: 

 

Datumstypen in Java & SQL
Vor Java 8
Modern
SQL

Zeitpunkt in UTC

java.util.Date java.sql.Timestamp

java.time.Instant

TIMESTAMP WITH TIME ZONE

Zeitpunkt mit Abweichung von UTC

- java.time.OffsetDateTime TIMESTAMP WITH TIME ZONE

Zeitpunkt mit Zeitzone

java.util.GregorianCalendar

java.time.ZonedDateTime

TIMESTAMP WITH TIME ZONE

Datum & Uhrzeit

- java.time.LocalDateTime TIMESTAMP WITHOUT TIME ZONE

Datum 

java.sql.Date 

java.time.LocalDate DATE 

Uhrzeit

java.sql.Time  java.time.LocalTime TIME WITHOUT TIME ZONE

Uhrzeit mit Abweichung

- java.time.OffsetTime TIME WITH TIME ZONE
 
 

Für eine schrittweise Migration kann mithilfe der Methode Date.from() aus einer Instanz von java.time.Instant ein java.util.Date-Objekt erzeugt werden. Dies erlaubt es Entwicklern, die neuen Funktionen und Vorteile der java.time API teilweise zu integrieren, während sie gleichzeitig die bestehende Codebasis unterstützt. Auch wenn so Interoperabilität ermöglicht wird, muss darauf geachtet werden bei der Konvertierung zwischen den Klassen von java.util.Date / java.sql.Date und java.time die richtigen Typen zu wählen, um Informationsverlust zu vermeiden.

Eine umfangreiche Testabdeckung ist essentiell, um sicherzustellen, dass alle Berechnungen und Darstellungen von Datumswerten den Erwartungen entsprechen. Dies gilt insbesondere für Anwendungen, welche unterschiedliche Zeitzonen unterstützen. 

 

Fazit 

Die Migration zur java.time API erfordert sorgfältige Überlegungen, insbesondere in Bezug auf Typen, Schnittstellen und Datenbankoperationen. Die Vorteile der neuen API überwiegen jedoch. Die klaren und sicheren Datums- und Zeitoperationen von java.time steigern die Entwicklungsgeschwindigkeit und verbessern die Wartbarkeit und Zuverlässigkeit. Somit ist die Migration eine wichtige Investition in die Zukunft ihrer Java-Anwendungen.

 

Um weitere Vorteile moderner Java-Versionen vorzustellen bieten wir eine Schulung zum Update auf Java 17 an: 

Zur Schulung

 


zurück zur Blogübersicht

Diese Beiträge könnten Sie ebenfalls interessieren

Keinen Beitrag verpassen – viadee Blog abonnieren

Jetzt Blog abonnieren!

Kommentare

Philipp Müller

Philipp Müller

Philipp Müller ist Java-Entwickler und IT-Berater bei der viadee IT-Unternehmensberatung. Er hat ein besonderes Interesse an Software-Architektur und IT-Security und arbeitet daran, effektive und sichere Softwarelösungen für Unternehmen zu entwickeln.
Philipp Müller bei LinkedIn