15.7 Wichtige Datum-Klassen im Überblick
Weil Datumsberechnungen verschlungene Gebilde sind, können wir den Entwicklern von Java dankbar sein, dass sie uns viele Klassen zur Datumsberechnung und -formatierung zur Verfügung stellen. Die Entwickler haben die Klassen so abstrakt gehalten, dass lokale Besonderheiten wie Ausgabeformatierung, Parsen, Zeitzonen oder Sommer- und Winterzeit in verschiedenen Kalendern möglich sind.
Bis zur Java-Version 1.1 stand zur Darstellung und Manipulation von Datumswerten ausschließlich die Klasse java.util.Date zur Verfügung. Diese hatte mehrere Aufgaben:
Erzeugung eines Datum/Zeit-Objekts aus Jahr, Monat, Tag, Minute und Sekunde
Abfrage von Tag, Monat, Jahr … mit der Genauigkeit von Millisekunden
Ausgabe und Verarbeitung von Datum-Zeichenketten
Da die Date-Klasse nicht ganz fehlerfrei und internationalisiert war, wurden im JDK 1.1 neue Klassen eingeführt:
Calendar nimmt sich der Aufgabe von Date an, zwischen verschiedenen Datumsrepräsentationen und Zeitskalen zu konvertieren. Die Unterklasse GregorianCalendar wird direkt erzeugt.
DateFormat zerlegt Datum-Zeichenketten und formatiert die Ausgabe. Auch Datumsformate sind vom Land abhängig, das Java durch Locale-Objekte darstellt, und von einer Zeitzone, die durch die Exemplare der Klasse TimeZone repräsentiert ist.
In Java 8 zog eine weitere Datumsbibliothek mit ganz neuen Typen ein. Endlich können auch Datum und Zeit getrennt repräsentiert werden:
LocalDate, LocalTime, LocalDateTime sind die temporalen Klassen für ein Datum, für eine Zeit und für eine Kombination aus Datum und Zeit.
Period und Duration stehen für Abstände.
15.7.1 Der 1.1.1970
Der 1.1.1970 war ein Donnerstag mit wegweisenden Änderungen: Die Briten freuten sich, dass die Volljährigkeit von 24 Jahren auf 18 Jahre fiel, und wie in jedem Jahr wurde das Beschneidungsfest gefeiert. Für uns ist aber eine technische Neuerung von Belang: Der 1.1.1970, 0:00:00 UTC heißt auch Unix-Epoche, und eine Unixzeit wird relativ zu diesem Zeitpunkt in Sekunden beschrieben. So kommen wir 100.000.000 Sekunden nach dem 1.1.1970 beim 3. März 1973 um 09:46:40 aus. Das Unix Billennium wurde bei 1.000.000.000 Sekunden nach dem 1.1.1970 gefeiert und repräsentiert den 9. September 2001, 01:46:40.
15.7.2 System.currentTimeMillis()
Auch für uns Java-Entwickler ist die Unixzeit von Bedeutung, denn viele Zeiten in Java sind relativ zu diesem Datum. Der Zeitstempel 0 bezieht sich auf den 1.1.1970 0:00:00 Uhr Greenwich-Zeit – das entspricht 1 Uhr nachts deutscher Zeit. Die Methode System.currentTimeMillis() liefert die vergangenen Millisekunden – nicht Sekunden! – relativ zum 1.1.1970, 00:00 Uhr UTC, wobei allerdings die Uhr des Betriebssystems nicht so genau gehen muss. Die Anzahl der Millisekunden wird in einem long repräsentiert, also in 64 Bit. Das reicht für etwa 300 Millionen Jahre.
[ ! ] Warnung
Die Werte von currentTimeMillis() sind nicht zwingend aufsteigend, da sich Java die Zeit vom Betriebssystem holt, und da kann sich die Systemzeit ändern. Der Benutzer kann die Zeit anpassen, oder ein Dienst wie das Network Time Protocol (NTP) übernimmt diese Aufgabe. Differenzen von currentTimeMillis()-Zeitstempeln sind dann komplett falsch und könnten sogar negativ sein. Eine Alternative ist nanoTime(), das keinen Bezugspunkt hat, genauer und immer aufsteigend ist.[ 243 ](Die Seite http://stackoverflow.com/questions/351565/system-currenttimemillis-vs-system-nanotime geht auf Details ein und verlinkt auf interne Implementierungen. )
15.7.3 Einfache Zeitumrechnungen durch TimeUnit
Eine Zeitdauer wird in Java oft durch Millisekunden ausgedrückt. 1.000 Millisekunden entsprechen 1 Sekunde, 1.000 × 60 Millisekunden 1 Minute usw. Diese ganzen großen Zahlen sind jedoch nicht besonders anschaulich, sodass zur Umrechnung TimeUnit-Objekte mit ihren toXXX(…)-Methoden genutzt werden. Java deklariert folgende Konstanten in TimeUnit: NANOSECONDS, MICROSECONDS, MILLISECONDS, DAYS, HOURS, SECONDS, MINUTES.
Jedes der Aufzählungselemente definiert die Umrechnungsmethoden toDays(…), toHours(…), toMicros(…), toMillis(…), toMinutes(…), toNanos(…), toSeconds(…); sie bekommen ein long und liefern ein long in der entsprechenden Einheit. Zudem gibt es zwei convert(…)-Methoden, die von einer Einheit in eine andere umrechnen.
[»] Beispiel
Konvertiere 23.746.387 Millisekunden in Stunden:
int v = 23_746_387;
System.out.println( TimeUnit.MILLISECONDS.toHours( v ) ); // 6
System.out.println( TimeUnit.HOURS.convert( v, TimeUnit.MILLISECONDS ) ); // 6
enum java.util.concurrent.TimeUnit
extends Enum<TimeUnit>
implements Serializable, Comparable<TimeUnit>
NANOSECONDS, MICROSECONDS, MILLISECONDS, SECONDS, MINUTES, HOURS, DAYS
Aufzählungselemente von TimeUnitlong toDays(long duration)
long toHours(long duration)
long toMicros(long duration)
long toMillis(long duration)
long toMinutes(long duration)
long toNanos(long duration)
long toSeconds(long duration)
long convert(long sourceDuration, TimeUnit sourceUnit)
Liefert sourceUnit.toXXX(sourceDuration), wobei XXX für die jeweilige Einheit steht. Beispielsweise liefert es HOURS.convert(sourceDuration, sourceUnit), dann sourceUnit.toHours(1). Die Lesbarkeit der Methode ist nicht optimal, daher sollten die anderen Methoden bevorzugt werden. Ergebnisse werden unter Umständen abgeschnitten, nicht gerundet. Gibt es einen Überlauf, folgt eine keine ArithmeticException.long convert(Duration duration)
Konvertiert die übergebene duration in die Zeiteinheit, die das aktuelle TimeUnit repräsentiert. So liefert TimeUnit.MINUTES.convert( Duration.ofHours(12) ) zum Beispiel 720. Damit sind etwa aunit.convert(Duration.ofNanos(n)) und aunit.convert(n, NANOSECONDS) gleich. Neu in Java 11.