Titel   Inhalt   Suchen   Index   DOC  Handbuch der Java-Programmierung, 7. Auflage
 <<    <     >    >>   API  Kapitel 17 - Utility-Klassen I

17.2 Die Klassen Date, Calendar und GregorianCalendar



In den Anfangsjahren von Java war zur Darstellung und Manipulation von Datumswerten die Klasse Date vorgesehen. Leider war sie nicht ganz fehlerfrei und aufgrund diverser Einschränkungen nur sehr bedingt zu gebrauchen. Ab der Version 1.1 des JDK gibt es neben Date die Klasse Calendar zur Verarbeitung von Datumswerten. Obgleich der Name den Anschein erwecken mag, dass ein Objekt vom Typ Calendar ein visueller Kalender ist, der als Komponente in GUI-basierten Programmen verwendet werden kann, ist dies nicht der Fall. Stattdessen stellt Calendar eine Kapselung für Date dar, deren Aufgabe es ist, ein Datums-/Uhrzeitobjekt zu realisieren und Methoden zur Konstruktion, zum Verändern und Auslesen von Datums-/Uhrzeitbestandteilen und für die Zeit- und Datumsarithmetik zur Verfügung zu stellen.

Zunächst ist Calendar nichts weiter als eine abstrakte Basisklasse. Sie enthält die Methoden, mit denen auf die einzelnen Elemente konkreter Kalenderklassen zugegriffen werden kann bzw. mit denen diese Klassen manipuliert werden können. Als einzige konkrete Ableitung von Calendar steht die Klasse GregorianCalendar zur Verfügung, die ein Datum nach dem hierzulande verwendeten gregorianischen Kalender implementiert. Die Komplexität der Klassen Calendar und GregorianCalendar kommt vor allem durch folgende Ursachen zustande:

In der Tat ist die Implementierung der Kalenderklassen des JDK komplex und war lange Zeit fehlerbehaftet. Sie wird insbesondere dadurch erschwert, dass ein Datumsobjekt nicht nur aus den einzelnen Feldern für Tag, Monat, Jahr usw. besteht, sondern zusätzlich einen ganzzahligen Wert des Typs long enthält, der das Datum als Anzahl der Millisekunden seit dem 1.1.1970 speichert. Beide Werte müssen auch nach Veränderungen einzelner Bestandteile des Datumsobjekts konsistent gehalten werden.

Auch die Bedienung der Kalenderklassen ist nicht so eingängig wie in vielen anderen Programmiersprachen. Hinderlich ist dabei oft die Tatsache, dass neben Datum und Uhrzeit grundsätzlich auch die Zeitzone mit betrachtet wird. Wir wollen in diesem Abschnitt einen pragmatischen Ansatz wählen und nur die wesentlichen Eigenschaften der beiden Klassen vorstellen. Fortgeschrittenere Themen wie Zeitzonenkalkulation oder Lokalisierung werden außen vor bleiben.

17.2.1 Konstruktoren

Da die Klasse Calendar abstrakt ist, müssen konkrete Datumsobjekte aus der Klasse GregorianCalendar erzeugt werden. Dazu stehen folgende Konstruktoren zur Verfügung:

public GregorianCalendar()

public GregorianCalendar(int year, int month, int date)

public GregorianCalendar(
   int year, int month, int date,
   int hour, int minute
)

public GregorianCalendar(
   int year, int month, int date,
   int hour, int minute, int second
)
java.util.GregorianCalendar

Der parameterlose Konstruktor initialisiert das Datumsobjekt mit dem aktuellen Datum und der aktuellen Uhrzeit. Die übrigen Konstruktoren weisen die als Parameter übergebenen Werte zu. Neben den hier vorgestellten Konstruktoren gibt es noch weitere, die es erlauben, die Zeitzone und Lokalisierung zu verändern. Standardmäßig werden die lokale Zeitzone und die aktuelle Lokalisierung verwendet.

17.2.2 Abfragen und Setzen von Datumsbestandteilen

Das Abfragen und Setzen von Datumsbestandteilen erfolgt mit den Methoden set und get:

public final int get(int field)

public final void set(int field, int value)
java.util.Calendar

get und set erwarten als erstes Argument einen Feldbezeichner, der angibt, auf welches der diversen Datums-/Zeitfelder des Objekts zugegriffen werden soll. Als Rückgabewert liefert get den Inhalt des angegebenen Felds; set schreibt den als zweiten Parameter value übergebenen Wert in das Feld hinein. Tabelle 17.1 enthält eine Übersicht der in Calendar vorgesehenen Feldbezeichner, ihrer Wertegrenzen und Bedeutung:

Feldbezeichner Minimalwert Maximalwert Bedeutung
Calendar.ERA 0 1 Ära
Calendar.YEAR 1 5,000,000 Jahr
Calendar.MONTH 0 11 Monat - 1
Calendar.WEEK_OF_YEAR 1 54 Woche im Jahr
Calendar.WEEK_OF_MONTH 1 6 Woche im Monat
Calendar.DAY_OF_MONTH 1 31 Tag im Monat
Calendar.DAY_OF_YEAR 1 366 Tag im Jahr
Calendar.DAY_OF_WEEK 1 7 Wochentag
Calendar.DAY_OF_WEEK_IN_MONTH -1 6 Wochentagswiederholung im Monat
Calendar.AM_PM 0 1 Vor-/nachmittags
Calendar.HOUR 0 12 Amerik. Stunde
Calendar.HOUR_OF_DAY 0 23 Stunde
Calendar.MINUTE 0 59 Minute
Calendar.SECOND 0 59 Sekunde
Calendar.MILLISECOND 0 999 Millisekunde
Calendar.ZONE_OFFSET -12*60*60*1000 12*60*60*1000 Zeitzonenoffset
Calendar.DST_OFFSET 0 1*60*60*1000 Sommerzeitoffset

Tabelle 17.1: Feldbezeichner der Klasse Calendar

Beim Lesen und Setzen der Felder gibt es einige Besonderheiten zu beachten. So wird beispielsweise der Monat nicht von 1 bis 12 gemessen, sondern von 0 bis 11 - ein ziemlich tückischer Fehler, auf den auch erfahrene Programmierer immer mal wieder hereinfallen! Das Feld ERA gibt an, ob das Datum vor Christi Geburt oder danach liegt. DAY_OF_WEEK geht von 1 = Sonntag bis 7 = Samstag, es stehen aber auch symbolische Konstanten zur Verfügung. ZONE_OFFSET und DST_OFFSET sind die Zeitzonen- und Sommerzeitabweichungen, die in Millisekunden gemessen werden.

 Hinweis 

Wir wollen uns die Verwendung der verschiedenen Felder an einem einfachen Beispiel ansehen. Das Programm zeigt auch die Verwendung einiger symbolischer Konstanten zur Darstellung von Wochentagen und der Ära (vor/nach Christi Geburt):

001 /* Listing1702.java */
002 
003 import java.util.*;
004 
005 public class Listing1702
006 {
007   public static void main(String[] args)
008   {
009     //Zuerst Ausgabe des aktuellen Datums
010     GregorianCalendar cal = new GregorianCalendar();
011     printCalendarInfo(cal);
012     System.out.println("---");
013 
014     //Nun Ausgabe der Informationen zum 22.6.1910,
015     //dem Geburtstag von Konrad Zuse
016     cal.set(Calendar.DATE, 22);
017     cal.set(Calendar.MONTH, 6 - 1);
018     cal.set(Calendar.YEAR, 1910);
019     printCalendarInfo(cal);
020   }
021 
022   public static void printCalendarInfo(GregorianCalendar cal)
023   {
024     //Aera
025     int value = cal.get(Calendar.ERA);
026     if (value == cal.BC) {
027       System.out.println("Aera.......: vor Christi Geburt");
028     } else if (value == cal.AD) {
029       System.out.println("Aera.......: Anno Domini");
030     } else {
031       System.out.println("Aera.......: unbekannt");
032     }
033     //Datum
034     System.out.println(
035       "Datum......: " +
036       cal.get(Calendar.DATE) + "." +
037       (cal.get(Calendar.MONTH)+1) + "." +
038       cal.get(Calendar.YEAR)
039     );
040     //Zeit
041     System.out.println(
042       "Zeit.......: " +
043       cal.get(Calendar.HOUR_OF_DAY) + ":" +
044       cal.get(Calendar.MINUTE) + ":" +
045       cal.get(Calendar.SECOND) + " (+" +
046       cal.get(Calendar.MILLISECOND) + " ms)"
047     );
048     //Zeit, amerikanisch
049     System.out.print(
050       "Am.Zeit....: " +
051       cal.get(Calendar.HOUR) + ":" +
052       cal.get(Calendar.MINUTE) + ":" +
053       cal.get(Calendar.SECOND)
054     );
055     value = cal.get(Calendar.AM_PM);
056     if (value == cal.AM) {
057       System.out.println(" AM");
058     } else if (value == cal.PM) {
059       System.out.println(" PM");
060     }
061     //Tag
062     System.out.println(
063       "Tag........: " +
064       cal.get(Calendar.DAY_OF_YEAR) + ". im Jahr"
065     );
066     System.out.println(
067       "             " +
068       cal.get(Calendar.DAY_OF_MONTH) + ". im Monat"
069     );
070     //Woche
071     System.out.println(
072       "Woche......: " +
073       cal.get(Calendar.WEEK_OF_YEAR) + ". im Jahr"
074     );
075     System.out.println(
076       "             " +
077       cal.get(Calendar.WEEK_OF_MONTH) + ". im Monat"
078     );
079     //Wochentag
080     System.out.print(
081       "Wochentag..: " +
082       cal.get(Calendar.DAY_OF_WEEK_IN_MONTH) +
083       ". "
084     );
085     value = cal.get(Calendar.DAY_OF_WEEK);
086     if (value == cal.SUNDAY) {
087       System.out.print("Sonntag");
088     } else if (value == cal.MONDAY) {
089       System.out.print("Montag");
090     } else if (value == cal.TUESDAY) {
091       System.out.print("Dienstag");
092     } else if (value == cal.WEDNESDAY) {
093       System.out.print("Mittwoch");
094     } else if (value == cal.THURSDAY) {
095       System.out.print("Donnerstag");
096     } else if (value == cal.FRIDAY) {
097       System.out.print("Freitag");
098     } else if (value == cal.SATURDAY) {
099       System.out.print("Samstag");
100     } else {
101       System.out.print("unbekannt");
102     }
103     System.out.println(" im Monat");
104     //Zeitzone
105     System.out.println(
106       "Zeitzone...: " +
107       cal.get(Calendar.ZONE_OFFSET)/3600000 +
108       " Stunden"
109     );
110     System.out.println(
111       "Sommerzeit.: " +
112       cal.get(Calendar.DST_OFFSET)/3600000 +
113       " Stunden"
114     );
115   }
116 }
Listing1702.java
Listing 17.2: Die Felder der Klasse Calendar

Das Programm erzeugt zunächst ein GregorianCalendar-Objekt für das aktuelle Tagesdatum und gibt die internen Feldwerte aus. Anschließend ändert es durch mehrfachen Aufruf von set das Datum in den 22.6.1910 ab und gibt die Felder erneut aus. Die Ausgabe des Programms lautet:

Aera.......: Anno Domini
Datum......: 24.6.2011
Zeit.......: 20:18:13 (+733 ms)
Am.Zeit....: 8:18:13 PM
Tag........: 175. im Jahr
             24. im Monat
Woche......: 25. im Jahr
             4. im Monat
Wochentag..: 4. Freitag im Monat
Zeitzone...: 1 Stunden
Sommerzeit.: 1 Stunden
---
Aera.......: Anno Domini
Datum......: 22.6.1910
Zeit.......: 20:18:13 (+733 ms)
Am.Zeit....: 8:18:13 PM
Tag........: 173. im Jahr
             22. im Monat
Woche......: 25. im Jahr
             4. im Monat
Wochentag..: 4. Mittwoch im Monat
Zeitzone...: 1 Stunden
Sommerzeit.: 0 Stunden

17.2.3 Vergleiche und Datums-/Zeitarithmetik

Die Methoden equals, before und after erlauben es, zwei Datumswerte auf ihre relative zeitliche Lage zueinander zu vergleichen:

public boolean equals(Object obj)

public boolean before(Object obj)

public boolean after(Object obj)
java.util.Calendar

Mit Hilfe der Methode add kann zu einem beliebigen Feld eines Calendar- oder GregorianCalendar-Objekts ein beliebiger positiver oder negativer Wert hinzugezählt werden:

public abstract void add(int field, int amount)
java.util.Calendar

Dabei ist es auch erlaubt, dass die Summe den für dieses Feld erlaubten Grenzwert über- bzw. unterschreitet. In diesem Fall wird der nächsthöherwertige Datums- bzw. Zeitbestandteil entsprechend angepasst.

Das folgende Programm konstruiert zunächst ein Datum für den 30.10.1908 und gibt es aus. Anschließend wird zunächst der Tag, dann der Monat und schließlich das Jahr je zweimal um 1 erhöht. Nach erfolgter Ausgabe wird die Änderung schrittweise wieder rückgängig gemacht und der ursprüngliche Wert wieder erzeugt:

001 /* Listing1703.java */
002 
003 import java.util.*;
004 
005 public class Listing1703
006 {
007   public static void main(String[] args)
008   {
009     GregorianCalendar cal   = new GregorianCalendar();
010     cal.set(Calendar.DATE, 30);
011     cal.set(Calendar.MONTH, 10 - 1);
012     cal.set(Calendar.YEAR, 1908);
013     showDate(cal);
014     addOne(cal, Calendar.DATE);
015     addOne(cal, Calendar.DATE);
016     addOne(cal, Calendar.MONTH);
017     addOne(cal, Calendar.MONTH);
018     addOne(cal, Calendar.YEAR);
019     addOne(cal, Calendar.YEAR);
020 
021     cal.add(Calendar.DATE, -2);
022     cal.add(Calendar.MONTH, -2);
023     cal.add(Calendar.YEAR, -2);
024     showDate(cal);
025   }
026 
027   public static void addOne(Calendar cal, int field)
028   {
029     cal.add(field,1);
030     showDate(cal);
031   }
032 
033   public static void showDate(Calendar cal)
034   {
035     String ret = "";
036     int    value = cal.get(Calendar.DAY_OF_WEEK);
037 
038     switch (value) {
039     case Calendar.SUNDAY:
040       ret += "Sonntag";
041       break;
042     case Calendar.MONDAY:
043       ret += "Montag";
044       break;
045     case Calendar.TUESDAY:
046       ret += "Dienstag";
047       break;
048     case Calendar.WEDNESDAY:
049       ret += "Mittwoch";
050       break;
051     case Calendar.THURSDAY:
052       ret += "Donnerstag";
053       break;
054     case Calendar.FRIDAY:
055       ret += "Freitag";
056       break;
057     case Calendar.SATURDAY:
058       ret += "Samstag";
059       break;
060     }
061     ret += ", den ";
062     ret += cal.get(Calendar.DATE) + ".";
063     ret += (cal.get(Calendar.MONTH)+1) + ".";
064     ret += cal.get(Calendar.YEAR);
065     System.out.println(ret);
066   }
067 }
Listing1703.java
Listing 17.3: Datumsarithmetik

Die Ausgabe des Programms lautet:

Freitag, den 30.10.1908
Samstag, den 31.10.1908
Sonntag, den 1.11.1908
Dienstag, den 1.12.1908
Freitag, den 1.1.1909
Samstag, den 1.1.1910
Sonntag, den 1.1.1911
Freitag, den 30.10.1908

Die formatierte Ausgabe von Datumswerten kann unter Verwendung der in verschiedenen Klassen implementierten Methode printf vereinfacht werden (siehe Abschnitt 12.6). Weitere Möglichkeiten werden im Zusammenhang mit der Internationalisierung von Java-Programmen in Abschnitt 18.4.3 erläutert.

 Tip 

17.2.4 Umwandlung zwischen Date und Calendar

In der Praxis ist es mitunter erforderlich, zwischen den beiden konkurrierenden Zeitdarstellungen der Klassen Date und Calendar hin- und herzuschalten. So ist beispielsweise der in JDBC (siehe Kapitel 44) häufig verwendete SQL-Datentyp java.sql.Date aus java.util.Date abgeleitet und repräsentiert ein Datum als Anzahl der Millisekunden seit dem 1.1.1970. Mit Hilfe der Methoden setTime und getTime können beide Darstellungen ineinander überführt werden:

public final Date getTime()

public final void setTime(Date date)
java.util.Calendar

Ein Aufruf von getTime liefert das Datum als Objekt des Typs Date. Soll das Datum aus einem vorhandenen Date-Objekt in ein Calendar-Objekt übertragen werden, kann dazu die Methode setTime aufgerufen werden. Die Klasse Date kann weiterhin dazu verwendet werden, auf die Anzahl der Millisekunden seit dem 1.1.1970 zuzugreifen:

public Date(long date)

public long getTime()
java.util.Date

Der Konstruktor erzeugt aus dem long ein Date-Objekt und die Methode getTime kann dazu verwendet werden, zu einem gegebenen Date-Objekt die Anzahl der Millisekunden seit dem 1.1.1970 zu ermitteln.


 Titel   Inhalt   Suchen   Index   DOC  Handbuch der Java-Programmierung, 7. Auflage, Addison Wesley, Version 7.0
 <<    <     >    >>   API  © 1998, 2011 Guido Krüger & Heiko Hansen, http://www.javabuch.de