Titel | Inhalt | Suchen | Index | DOC | Handbuch der Java-Programmierung, 5. Auflage |
<< | < | > | >> | API | Kapitel 16 - Utility-Klassen I |
Bis zum JDK 1.0.2 war die Klasse Date zur Darstellung und Manipulation von Datumswerten 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 richtig. Statt dessen stellt Calendar eine Kapselung für Date dar, deren Aufgabe es ist, ein Datum-/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.
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.
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 Objektes zugegriffen werden soll. Als Rückgabewert liefert get den Inhalt des angegebenen Feldes; set schreibt den als zweiten Parameter value übergebenen Wert in das Feld hinein. Tabelle 16.1 gibt 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 16.1: Feldbezeichner der Klasse Calendar
Hierbei gibt es einige Besonderheiten zu beachten. So wird beispielsweise der Monat nicht von 1 bis 12 gemessen, sondern von 0 bis 11. 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. |
|
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 /* Listing1602.java */ 002 003 import java.util.*; 004 005 public class Listing1602 006 { 007 public static void main(String[] args) 008 { 009 //Zuerst Ausgabe des aktuellen Datums 010 GregorianCalendar cal = new GregorianCalendar(); 011 //cal.setTimeZone(TimeZone.getTimeZone("ECT")); 012 printCalendarInfo(cal); 013 System.out.println("---"); 014 015 //Nun Ausgabe der Informationen zum 22.6.1910, 016 //dem Geburtstag von Konrad Zuse 017 cal.set(Calendar.DATE, 22); 018 cal.set(Calendar.MONTH, 6 - 1); 019 cal.set(Calendar.YEAR, 1910); 020 printCalendarInfo(cal); 021 //cal.setTime(cal.getTime()); 022 } 023 024 public static void printCalendarInfo(GregorianCalendar cal) 025 { 026 //Aera 027 int value = cal.get(Calendar.ERA); 028 if (value == cal.BC) { 029 System.out.println("Aera.......: vor Christi Geburt"); 030 } else if (value == cal.AD) { 031 System.out.println("Aera.......: Anno Domini"); 032 } else { 033 System.out.println("Aera.......: unbekannt"); 034 } 035 //Datum 036 System.out.println( 037 "Datum......: " + 038 cal.get(Calendar.DATE) + "." + 039 (cal.get(Calendar.MONTH)+1) + "." + 040 cal.get(Calendar.YEAR) 041 ); 042 //Zeit 043 System.out.println( 044 "Zeit.......: " + 045 cal.get(Calendar.HOUR_OF_DAY) + ":" + 046 cal.get(Calendar.MINUTE) + ":" + 047 cal.get(Calendar.SECOND) + " (+" + 048 cal.get(Calendar.MILLISECOND) + " ms)" 049 ); 050 //Zeit, amerikanisch 051 System.out.print( 052 "Am.Zeit....: " + 053 cal.get(Calendar.HOUR) + ":" + 054 cal.get(Calendar.MINUTE) + ":" + 055 cal.get(Calendar.SECOND) 056 ); 057 value = cal.get(Calendar.AM_PM); 058 if (value == cal.AM) { 059 System.out.println(" AM"); 060 } else if (value == cal.PM) { 061 System.out.println(" PM"); 062 } 063 //Tag 064 System.out.println( 065 "Tag........: " + 066 cal.get(Calendar.DAY_OF_YEAR) + ". im Jahr" 067 ); 068 System.out.println( 069 " " + 070 cal.get(Calendar.DAY_OF_MONTH) + ". im Monat" 071 ); 072 //Woche 073 System.out.println( 074 "Woche......: " + 075 cal.get(Calendar.WEEK_OF_YEAR) + ". im Jahr" 076 ); 077 System.out.println( 078 " " + 079 cal.get(Calendar.WEEK_OF_MONTH) + ". im Monat" 080 ); 081 //Wochentag 082 System.out.print( 083 "Wochentag..: " + 084 cal.get(Calendar.DAY_OF_WEEK_IN_MONTH) + 085 ". " 086 ); 087 value = cal.get(Calendar.DAY_OF_WEEK); 088 if (value == cal.SUNDAY) { 089 System.out.print("Sonntag"); 090 } else if (value == cal.MONDAY) { 091 System.out.print("Montag"); 092 } else if (value == cal.TUESDAY) { 093 System.out.print("Dienstag"); 094 } else if (value == cal.WEDNESDAY) { 095 System.out.print("Mittwoch"); 096 } else if (value == cal.THURSDAY) { 097 System.out.print("Donnerstag"); 098 } else if (value == cal.FRIDAY) { 099 System.out.print("Freitag"); 100 } else if (value == cal.SATURDAY) { 101 System.out.print("Samstag"); 102 } else { 103 System.out.print("unbekannt"); 104 } 105 System.out.println(" im Monat"); 106 //Zeitzone 107 System.out.println( 108 "Zeitzone...: " + 109 cal.get(Calendar.ZONE_OFFSET)/3600000 + 110 " Stunden" 111 ); 112 System.out.println( 113 "Sommerzeit.: " + 114 cal.get(Calendar.DST_OFFSET)/3600000 + 115 " Stunden" 116 ); 117 } 118 } |
Listing1602.java |
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......: 28.4.2000
Zeit.......: 17:55:0 (+590 ms)
Am.Zeit....: 5:55:0 PM
Tag........: 119. im Jahr
28. im Monat
Woche......: 17. im Jahr
4. im Monat
Wochentag..: 4. Freitag im Monat
Zeitzone...: 1 Stunden
Sommerzeit.: 1 Stunden
---
Aera.......: Anno Domini
Datum......: 22.6.1910
Zeit.......: 17:55:0 (+590 ms)
Am.Zeit....: 5:55:0 PM
Tag........: 173. im Jahr
22. im Monat
Woche......: 25. im Jahr
4. im Monat
Wochentag..: 4. Mittwoch im Monat
Zeitzone...: 1 Stunden
Sommerzeit.: 1 Stunden
Wie man sieht, werden sowohl Datums- als auch Zeitwerte korrekt ausgegeben. Damit dieses und vergleichbare Programme auch in den JDK-1.1-Versionen korrekt laufen, sind einige Besonderheiten zu beachten:
|
|
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 /* Listing1603.java */ 002 003 import java.util.*; 004 005 public class Listing1603 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 } |
Listing1603.java |
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
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 42) 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, 5. Auflage, Addison Wesley, Version 5.0.2 |
<< | < | > | >> | API | © 1998, 2007 Guido Krüger & Thomas Stark, http://www.javabuch.de |