CodeGym /Java-Blog /Germany /Die Zeit im Blick behalten: Java Date und Calendar
Autor
Milan Vucic
Programming Tutor at Codementor.io

Die Zeit im Blick behalten: Java Date und Calendar

Veröffentlicht in der Gruppe Germany
Hallo! Heute beginnen wir mit einem neuen Datentyp zu arbeiten, dem wir bisher noch nicht begegnet sind, und zwar mit Datumsangaben. Ich muss wohl nicht erklären, was ein Datum ist. :) Im Prinzip könnten wir das Datum in Java zusammen mit der Uhrzeit in einem gewöhnlichen Java-String speichern. Die Zeit im Blick behalten: Java Date und Calendar - 1

public class Main {
   public static void main(String[] args) {

       String date = "June 11, 2018";
       System.out.println(date);
   }
}
Aber diese Vorgehensweise hat viele Nachteile. Deshalb lernen wir Date und Calendar kennen. Die String-Klasse wurde für die Arbeit mit Text entwickelt und ihre Methoden sind für diese Aufgabe ausgelegt. Wenn wir ein Datum auf irgendeine Weise manipulieren müssen (z. B. 2 Stunden addieren), funktioniert String nicht so gut. Oder wenn wir beim Kompilieren des Programms das aktuelle Datum und die aktuelle Uhrzeit anzeigen wollen. String bringt uns auch hier nicht weiter: Wenn du den Code schreibst und ausführst, hat sich die Uhrzeit geändert und die Konsole zeigt die falschen Werte an. Aus diesem Grund haben die Entwickler von Java verschiedene Klassen für die Arbeit mit Datumsangaben und Uhrzeiten bereitgestellt. Die erste davon ist java.util.Date

Java Date

Wir haben ihren vollständigen Namen angegeben, weil ein anderes Java-Paket die Klasse java.sql.Date enthält. Die darfst du nicht verwechseln! Zuerst musst du über die Klasse wissen, dass sie das Datum (DateTime) als die Anzahl der Millisekunden speichert, die seit dem 1. Januar 1970 vergangen sind. Dieses Zeitsystem hat sogar seinen eigenen Namen: „Unix-Zeit“ Ein ganz schön interessantes Konzept, oder? :) Die zweite Sache, die du dir merken solltest, ist diese: Wenn du ein Date-Objekt mit dem Standardkonstruktor erstellst, entspricht das Ergebnis dem aktuellen Datum und der Uhrzeit zum Zeitpunkt der Objekterstellung. Erinnerst du dich, dass wir gesagt haben, dass ein als String angegebenes Datum mit einer solchen Aufgabe Schwierigkeiten haben würde? Die Klasse Date erledigt das mühelos.

public class Main {
   public static void main(String[] args) {

       Date date = new Date();
       System.out.println(date);
   }
}
Führe diesen Code mehrmals aus, dann siehst du, wie sich die Uhrzeit immer wieder ändert. :) Dies ist möglich, weil die Zeit in Millisekunden gespeichert ist: Das sind extrem kleine Zeiteinheiten, daher sind die Ergebnisse sehr genau. Die Klasse Date hat einen weiteren Konstruktor: Du kannst die exakte Anzahl von Millisekunden seit 00:00 Uhr am 1. Januar 1970 an das gewünschte Datum übergeben, und ein entsprechendes Datumsobjekt wird erstellt:

public class Main {
   public static void main(String[] args) {

       Date date = new Date(1212121212121L);
       System.out.println(date);
   }
}
Konsolenausgabe:
Fri May 30 04:20:12 GMT 2008
Wir erhalten das Datum 30. Mai 2008. „Fri“ gibt den Wochentag an (Friday; Freitag), und GMT ist die Zeitzone (Greenwich Mean Time). Millisekunden werden als long-Werte übergeben, da die Anzahl der Millisekunden normalerweise nicht in ein int passt. Doch welche Operationen mit Datumsangaben müssen wir vielleicht durchführen? Am offensichtlichsten ist natürlich der Vergleich. Um festzustellen, ob ein Datum vor oder nach einem anderen liegt. Dies kann auf verschiedene Arten durchgeführt werden. Du kannst zum Beispiel die Methode Date.getTime() aufrufen, die die Anzahl der Millisekunden zurückgibt, die seit Mitternacht des 1. Januar 1970 vergangen sind. Rufe sie einfach für zwei Date-Objekte auf und vergleiche die Ergebnisse:

public class Main {
   public static void main(String[] args) {

       Date date1 = new Date();

       Date date2 = new Date();

       System.out.println((date1.getTime() > date2.getTime())?
               "date1 is later than date2" : "date1 is earlier than date2");
   }
}
Ausgabe:
date1 ist früher als date2
Aber es gibt auch einen einfacheren Weg, z.B. mit speziellen Methoden der Date-Klasse: before(), after() und equals(). Sie alle geben einen booleschen Wert zurück. Die Methode before() prüft, ob unser Datum vor dem Datum liegt, das als Argument übergeben wurde:

public class Main {
   public static void main(String[] args) throws InterruptedException {

       Date date1 = new Date();

       Thread.sleep(2000);// Suspend the program for 2 seconds
       Date date2 = new Date();

       System.out.println(date1.before(date2));
   }
}
Konsolenausgabe:
true
Auf die gleiche Weise prüft die Methode after(), ob unser Datum nach dem Datum liegt, das als Argument übergeben wurde:

public class Main {
   public static void main(String[] args) throws InterruptedException {

       Date date1 = new Date();

       Thread.sleep(2000);// Suspend the program for 2 seconds
       Date date2 = new Date();

       System.out.println(date1.after(date2));
   }
}
Konseolenausgabe:
false
In unseren Beispielen halten das Programm für 2 Sekunden an, damit die beiden Datumsangaben garantiert unterschiedlich sind. Auf schnellen Computern liegt die Zeit zwischen dem Erstellen von date1 und date2 möglicherweise unter einer Millisekunde, sodass sowohl before() als auch after() false zurückgeben würden. Aber in diesem Fall wird die Methode equals() true zurückgeben! Schließlich vergleicht sie für jedes Datum die Anzahl der Millisekunden seit 00:00 Uhr am 1. Januar 1970. Die Objekte werden nur dann als gleich betrachtet, wenn sie auf die Millisekunde genau übereinstimmen:

public static void main(String[] args) {

   Date date1 = new Date();
   Date date2 = new Date();

   System.out.println(date1.getTime());
   System.out.println(date2.getTime());

   System.out.println(date1.equals(date2));
}
Hier noch eine weitere Sache, auf die du achten musst. Wenn du die Dokumentation für die Klasse Date auf der Oracle-Website öffnest, wirst du sehen, dass viele ihrer Methoden und Konstruktoren als Deprecated markiert wurden (d. h. nicht zur Verwendung empfohlen werden). Folgendes sagen die Entwickler von Java über Bestandteile von Klassen, die als Deprecated eingestuft wurden:
„Ein Programmelement mit dem Vermerk @Deprecated sollten Programmierer nicht verwenden, weil es entweder gefährlich ist oder weil es eine bessere Alternative gibt.“
Das heißt aber nicht, dass diese Methoden überhaupt nicht eingesetzt werden können. Wenn du versuchts, Methoden, die als Deprecated eingestuft wurden, in einer IDE zu verwenden, wird es wahrscheinlich funktionieren. Sieh dir zum Beispiel die als Deprecated eingestufte Methode Date.getHours() an, die die Anzahl der Stunden zurückgibt, die mit einem Date-Objekt verbunden sind.

public static void main(String[] args) {

   Date date1 = new Date();

   System.out.println(date1.getHours());
}
Wenn du den Code um 14:21 (14:21 Uhr) startest, wird die Zahl 14 angezeigt. Wie du siehst, ist die Deprecated-Methode durchgestrichen, aber sie funktioniert immer noch. Diese Methoden werden nicht entfernt, damit der riesige Bestand an existierendem Code, der sie benutzt, nicht zerstört wird. Mit anderen Worten, diese Methoden sind weder „kaputt“ noch „entfernt“. Sie werden einfach nicht zur Benutzung empfohlen, weil es eine bessere Alternative gibt. Übrigens wird diese Alternative in der Dokumentation ausdrücklich genannt:
Die Zeit im Blick behalten: Date und Calendar - 2
Die meisten Methoden der Date-Klasse wurden in die verbesserte und erweiterte Calendar-Klasse verschoben. Als nächstes werden wir uns mit dieser Klasse vertraut machen. :)

Java Calendar

JDK 1.1 hat eine neue Klasse eingeführt: Calendar. Sie machte das Arbeiten mit Datumsangaben in Java etwas einfacher als zuvor. Die einzige Implementierung der Klasse Calendar, mit der wir arbeiten werden, ist die Klasse GregorianCalendar. Sie implementiert den Gregorianischen Kalender, der von den meisten Ländern der Welt angewandt wird. Ihr Hauptvorteil ist, dass sie mit Datumsangaben in einem komfortableren Format arbeiten kann. Zum Beispiel kann sie:
  • Einen Monat oder Tag zum aktuellen Datum hinzufügen
  • Prüfen, ob das Jahr ein Schaltjahr ist;
  • Einzelne Bestandteile des Datums zurückgeben (z. B. die Monatsnummer aus einem ganzen Datum extrahieren)
  • Sie enthält zudem ein sehr praktisches System von Konstanten (viele davon werden wir weiter unten sehen).
Eine weitere wichtige Erweiterung der Klasse Calendar ist ihre Konstante Calendar.ERA: Du kannst ein Datum vor der christlichen Zeitrechnung (BC – Vor Christus) oder nach der christlichen Zeitrechnung (AD – Nach Christus) angeben. Sehen wir uns das alles mit Beispielen an. Wir erstellen ein calendar-Objekt mit dem Datum 25. Januar 2017:

public static void main(String[] args) {

  Calendar calendar = new GregorianCalendar(2017, 0 , 25);
}
In der Klasse Calendar (wie auch in der Klasse Date) beginnen die Monate bei 0, also übergeben wir die Zahl 0 als zweites Argument. Wenn wir mit der Calendar-Klasse arbeiten, müssen wir beachten, dass es genau das ist: ein Kalender, kein einzelnes Datum. Ein Datum ist lediglich eine Zahlenfolge, die ein bestimmtes Zeitintervall angibt. Ein Kalender ist ein komplettes System, mit dem du eine Menge anstellen kannst. :) Ganz offensichtlich wird das, wenn du das Calendar-Objekt anzeigen lässt: Ausgabe:
java.util.GregorianCalendar[time=?,areFieldsSet=false,areAllFieldsSet=false,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Europe/London",offset=0,dstSavings=0,useDaylight=false,transitions=79,lastRule=null],firstDayOfWeek=2,minimalDaysInFirstWeek=1,ERA=?,YEAR=2017,MONTH=0,WEEK_OF_YEAR=?,WEEK_OF_MONTH=?,DAY_OF_MONTH=25,DAY_OF_YEAR=?,DAY_OF_WEEK=?,DAY_OF_WEEK_IN_MONTH=?,AM_PM=0,HOUR=0,HOUR_OF_DAY=0,MINUTE=0,SECOND=0,MILLISECOND=?,ZONE_OFFSET=?,DST_OFFSET=?]
Sieh dir die ganzen Informationen an! Ein Kalender hat eine ganze Menge Eigenschaften, die ein normales Datum nicht hat, und alle werden angezeigt (so funktioniert die toString()-Methode in der Calendar-Klasse). Wenn du nur ein einfaches Datum aus dem Kalender abrufen möchtest, d. h. ein Date-Objekt, dann verwende die Methode Calendar.getTime() (der Name ist nicht besonders logisch, aber was will man machen?)

public static void main(String[] args) {

   Calendar calendar = new GregorianCalendar(2017, 0 , 25);
   Date date = calendar.getTime();
   System.out.println(date);
}
Ausgabe:
Wed Jan 25 00:00:00 GMT 2017
Jetzt haben wir uns den Kalender geschnappt und ihn auf ein gewöhnliches Datum „reduziert“. Weiter geht‘s. Zusätzlich zur Angabe der Monate durch ihre Zahl kannst du auch die konstanten Feldwerte der Klasse Calendar verwenden. Diese Konstanten sind statische Felder der Calendar-Klasse mit einem voreingestellten Wert, der nicht geändert werden kann. Das ist sogar eine noch bessere Option, weil sie die Lesbarkeit deines Codes verbessert.

public static void main(String[] args) {
   GregorianCalendar calendar = new GregorianCalendar(2017, Calendar.JANUARY , 25);
}
Calendar.JANUARY ist eine der Konstanten, die die Monate des Jahres repräsentieren. Durch die Verwendung dieser benannten Konstanten wird niemand vergessen, dass zum Beispiel die Zahl 3 für April steht, und nicht für den dritten Monat, den wir gerne März nennen. Schreibe einfach Calendar.APRIL und das war‘s. :) Alle Kalenderfelder (Zahl, Monat, Minuten, Sekunden usw.) können mit der Methode set() einzeln angegeben werden. Diese Methode ist sehr praktisch, denn die Klasse Calendar besitzt eine Konstante für jedes Feld, und der resultierende Code ist sehr leicht zu lesen. Im letzten Beispiel haben wir ein Datum erstellt, aber keine Uhrzeit dafür festgelegt. Stellen wir die Zeit auf 19:42:12 ein.

public static void main(String[] args) {
   Calendar calendar = new GregorianCalendar();
   calendar.set(Calendar.YEAR, 2017);
   calendar.set(Calendar.MONTH, 0);
   calendar.set(Calendar.DAY_OF_MONTH, 25);
   calendar.set(Calendar.HOUR_OF_DAY, 19);
   calendar.set(Calendar.MINUTE, 42);
   calendar.set(Calendar.SECOND, 12);

   System.out.println(calendar.getTime());
}
Ausgabe:
Wed Jan 25 19:42:12 GMT 2017
Wir rufen die Methode set() auf und übergeben eine Konstante (abhängig von dem Feld, das wir ändern wollen) und den neuen Wert für das Feld. Es zeigt sich, dass diese set()-Methode eine Art „Super-Setter“ ist, der weiß, wie man den Wert nicht nur für ein Feld, sondern für viele Felder setzen kann. :) Die Klasse Calendar verwendet die Methode add(), um Werte zu addieren und zu subtrahieren. Du übergibst das Feld, das du ändern möchtest, und eine Zahl (wie viel du zum/vom aktuellen Wert addieren/abziehen möchtest). Nehmen wir zum Beispiel ein Datum, das 2 Monate vor dem Erstellungsdatum liegt:

public static void main(String[] args) {
   Calendar calendar = new GregorianCalendar(2017, Calendar.JANUARY , 25);
   calendar.set(Calendar.HOUR, 19);
   calendar.set(Calendar.MINUTE, 42);
   calendar.set(Calendar.SECOND, 12);

   calendar.add(Calendar.MONTH, -2); // To subtract, pass a negative number
   System.out.println(calendar.getTime());
}
Ausgabe:
Fri Nov 25 19:42:12 GMT 2016
Sehr gut! Wir haben das Datum von vor 2 Monaten erhalten. Dadurch hat sich nicht nur der Monat geändert, sondern auch das Jahr – von 2017 auf 2016. Selbstverständlich wird bei der Datumsumstellung das aktuelle Jahr automatisch berechnet, ohne dass du es manuell nachverfolgen musst. Wenn du dieses Verhalten jedoch aus irgendeinem Grund deaktivieren musst, kannst du das tun. Die roll()-Methode kann Werte addieren und subtrahieren , ohne die übrigen Werte zu beeinflussen. Zum Beispiel so:

public static void main(String[] args) {
   Calendar calendar = new GregorianCalendar(2017, Calendar.JANUARY , 25);
   calendar.set(Calendar.HOUR, 10);
   calendar.set(Calendar.MINUTE, 42);
   calendar.set(Calendar.SECOND, 12);

   calendar.roll(Calendar.MONTH, -2);
   System.out.println(calendar.getTime());
}
Wir haben genau dasselbe gemacht wie im vorhergehenden Beispiel: Wir haben 2 Monate vom aktuellen Datum abgezogen. Aber jetzt macht der Code etwas anderes: Der Monat hat sich von Januar auf November geändert, aber das Jahr bleibt unverändert – 2017! Ausgabe:
Sat Nov 25 10:42:12 GMT 2017
Weiter geht‘s. Wie bereits erwähnt, können wir alle Calendar-Felder separat abrufen. Wir tun dies mit der get() -Methode:

public static void main(String[] args) {
   GregorianCalendar calendar = new GregorianCalendar(2017, Calendar.JANUARY , 25);
   calendar.set(Calendar.HOUR, 10);
   calendar.set(Calendar.MINUTE, 42);
   calendar.set(Calendar.SECOND, 12);

   System.out.println("Year: " + calendar.get(Calendar.YEAR));
   System.out.println("Month: " + calendar.get(Calendar.MONTH));
   System.out.println("Week in the month: " + calendar.get(Calendar.WEEK_OF_MONTH));// Week in this month?

   System.out.println("Day: " + calendar.get(Calendar.DAY_OF_MONTH));

   System.out.println("Hours: " + calendar.get(Calendar.HOUR));
   System.out.println("Minutes: " + calendar.get(Calendar.MINUTE));
   System.out.println("Seconds: " + calendar.get(Calendar.SECOND));
   System.out.println("Milliseconds: " + calendar.get(Calendar.MILLISECOND));

}
Ausgabe:
Jahr: 2017 Monat: 0 Woche im Monat: 5 Tag: 25 Stunden: 10 Minuten: 42 Sekunden: 12 Millisekunden: 0
Neben dem „Super-Setter“ der Calendar-Klasse gibt es auch einen „Super-Getter“: :) Ein weiterer interessanter Aspekt dieser Klasse ist natürlich die Arbeit mit Zeiträumen. Um ein „v. Chr.“-Datum zu erstellen, musst du das Feld Calendar.ERA verwenden. Als Beispiel wollen wir ein Datum für die Schlacht von Cannae festlegen, in der Hannibal die römische Armee besiegte. Dies geschah am 2. August 216 v. Chr:

public static void main(String[] args) {
   GregorianCalendar cannae = new GregorianCalendar(216, Calendar.AUGUST, 2);
   cannae.set(Calendar.ERA, GregorianCalendar.BC);

   DateFormat df = new SimpleDateFormat("MMM dd, yyy GG");
   System.out.println(df.format(cannae.getTime()));
}
Hier haben wir die Klasse SimpleDateFormat verwendet, um das Datum in einem für uns leichter verständlichen Format anzuzeigen (die Buchstaben „GG“ bedeuten, dass wir den Zeitraum angezeigt haben möchten). Ausgabe:
Aug. 02, 216 BC.
Die Klasse Calendar verfügt über viele weitere Methoden und Konstanten. Diese kannst du in der Dokumentation nachlesen. Wenn dir dieses Datumsformat Sat Nov 25 10:42:12 GMT 2017 nicht gefällt, dann kannst du SimpleDateFormat verwenden, um es an deine Wünsche anzupassen.

public static void main(String[] args) {

   SimpleDateFormat dateFormat = new SimpleDateFormat("EEEE, MMMM d, yyyy");
   Calendar calendar = new GregorianCalendar(2017, Calendar.JANUARY , 25);
   calendar.set(Calendar.HOUR, 10);
   calendar.set(Calendar.MINUTE, 42);
   calendar.set(Calendar.SECOND, 12);

   calendar.roll(Calendar.MONTH, -2);
   System.out.println(dateFormat.format(calendar.getTime()));
}
Ausgabe:
Saturday, November 25, 2017
Das ist viel besser, oder? :)
Dieser Beitrag ist auf Englisch verfügbar.
Read the English version of this article about the Date class and Calendar class. Programmers need to be good at time management!
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION