Galileo Computing < openbook >
Galileo Computing - Professionelle Buecher. Auch fuer Einsteiger.
Galileo Computing - Professionelle Buecher. Auch fuer Einsteiger.


Java ist auch eine Insel von Christian Ullenboom
Buch: Java ist auch eine Insel (Galileo Computing)
gp Kapitel 10 Raum und Zeit
gp 10.1 Greenwich Mean Time (GMT)
gp 10.2 Wichtige Datum-Klassen im Überblick
gp 10.3 Zeitzonen und Sprachen der Länder
gp 10.3.1 Zeitzonen durch die Klasse TimeZone repräsentieren
gp 10.4 Sprachen der Länder
gp 10.4.1 Sprachen in Java über Locale-Objekte
gp 10.5 Einfache Übersetzung durch ResourceBundle-Objekte
gp 10.6 Die Klasse Date
gp 10.6.1 Objekte erzeugen und Methoden nutzen
gp 10.7 Calendar und GregorianCalendar
gp 10.7.1 Die abstrakte Klasse Calendar
gp 10.7.2 Der gregorianische Kalender
gp 10.8 Formatieren der Datumsangaben
gp 10.8.1 Mit DateFormat und SimpleDateFormat formatieren
gp 10.8.2 Parsen von Datumswerten
gp 10.8.3 Parsen und Formatieren ab bestimmten Positionen


Galileo Computing

10.7 Calendar und GregorianCalendardowntop

Über Methoden von Calendar ist es möglich, Datum und Uhrzeit in den einzelnen Komponenten wie Jahr, Monat, Tag, Minute, Sekunde zu verändern. Da Calendar eine abstrakte Basisklasse ist, können erst Unterklassen die Attribute und Methoden richtig benutzen. Unter dem Java SDK ist bisher nur die Unterklasse GregorianCalendar implementiert, deren Exemplare Daten und Zeitpunkte gemäß dem gregorianischen Kalender verkörpern. Unter http://oss.software.ibm.com/icu4j/ finden sich im Paket com.ibm.util auch die Klassen ChineseCalendar, GregorianCalendar, BuddhistCalendar, JapaneseCalendar, HebrewCalendar, IslamicCalendar.


Galileo Computing

10.7.1 Die abstrakte Klasse Calendardowntop

Die Klasse Calendar besitzt nur wenige statische Funktionen, die sofort genutzt werden können. Eine wichtige Klassen-Methode ist getInstance(), um ein benutzbares Objekt zu bekommen. Die Methode gibt ein Objekt vom Typ GregorianCalendar zurück, und die Werte sind mit der aktuellen Zeit gesetzt.


abstract class java.util.Calendar
implements Serializable, Cloneable

gp static Calendar getInstance()
Liefert einen Calendar in der Ausprägung von GregorianCalendar mit der eingestellten Zeitzone und Lokalisierung zurück.

Abbildung
Hier klicken, um das Bild zu Vergrößern

Neben der parameterlosen Variante von getInstance() gibt es drei weitere Varianten, denen ein TimeZone-Objekt und Locale-Objekt mit übergeben werden kann. Damit kann dann der Kalender auf eine spezielle Zeitzone und einen Landstrich zugeschnitten werden. Da ein Kalender unter verschiedenen Orten installiert sein kann, gibt getAvailableLocales() eine Aufzählung von Locale-Objekten zurück.


Galileo Computing

10.7.2 Der gregorianische Kalendertoptop

Die abstrakte Klasse Calendar wird durch die Klasse GregorianCalendar erweitert. In der jetzigen Implementierung deckt diese Klasse vom Jahr 4716 vor unserer Zeitrechnung bis zum Jahre 5.000.000 alles korrekt ab. Sieben Konstruktoren stehen zur Verfügung; vier davon sehen wir uns an:


class java.util.GregorianCalendar
extends Calendar

gp GregorianCalendar()
Erzeugt ein standardmäßiges GregorianCalendar-Objekt mit der aktuellen Zeit in der voreingestellten Zeitzone und Lokalisierung.
gp GregorianCalendar( int year, int month, int date )
Erzeugt ein GregorianCalendar-Objekt in der voreingestellten Zeitzone und Lokalisierung. Das Datum wird durch Jahr, Monat (der zwischen 0 und 11 und nicht zwischen 1 und 12 liegt) und Tag festgelegt.
gp GregorianCalendar( int year, int month, int date, int hour, int minute )
Erzeugt ein GregorianCalendar-Objekt in der voreingestellten Zeitzone und Lokalisierung. Das Datum wird durch Jahr, Monat (0 <= month <= 11 ), Tag, Stunde und Minute festgelegt.
gp GregorianCalendar( int year, int month, int date,
int hour, int minute, int second )
Erzeugt ein GregorianCalendar-Objekt in der voreingestellten Zeitzone und Lokalisierung. Das Datum wird durch Jahr, Monat (0 <= month <= 11), Tag, Stunde, Minute und Sekunde festgelegt.

Neben den hier aufgeführten Konstruktoren gibt es noch weitere, die es erlauben, die Zeitzone und Lokalisierung zu ändern. Standardmäßig eingestellt ist die lokale Zeitzone und die aktuelle Lokalisierung. Ist einer der Parameter im falschen Bereich, so wird eine IllegalArgumentException ausgelöst. Damit bei 0 und 1 Anfrageprobleme vermieden werden, sollten die Konstanten JANUARY (0), FEBRUARY, MARCH, APRIL, MAY, JUNE, JULY, AUGUST, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER (11) verwendet werden. Die spezielle Variable UNDECIMBER (12) steht für den dreizehnten Monat, der etwa bei einem Mondkalender anzutreffen ist.


Beispiel Bestimme die Anzahl der Tage, die seit einem bestimmten Tag, Monat und Jahr vergangen sind.
int date  = 1;    // 1.
int month = 0;    // Januar
int year  = 1900;
long dt = new GregorianCalendar(year,month,date).getTimeInMillis();
long days = (System.currentTimeMillis() - dt) / 86400000;

Probleme beim Standard-Konstruktor

Leider haben wir mit dem Standard-Konstruktor von GregorianCalendar ein kleines Problem. Zur Klärung sehen wir uns die Implementierung einmal genauer an:

public GregorianCalendar()
{
  this( TimeZone.getDefault(), Locale.getDefault() );
}

Wird der gregorianische Kalender ohne Zeitzonenangabe konstruiert, wird die statische Methode getDefault() von der Klasse TimeZone aufgerufen. In der Implementierung von getDefault() finden wir dann Folgendes:

ID = System.getProperty( "user.timezone", "GMT" );

In den Systemeigenschaften wird also nach user.timezone gefragt, und falls dieser Eintrag dann nicht existiert, wird die Zeitzone GMT angenommen; wir hatten das Problem schon einmal angesprochen. Wenn bei Rechnern diese nicht auf European Central Time, kurz ECT, steht - wie es bei uns in Deutschland sein sollte -, gehen alle Zeitangaben eine Stunde nach. Um dieses Problem zu beheben, sollte die korrekte Zeitzone mit dem Konstruktor GregorianCalendar(TimeZone) oder mit der Methode setTimeZone()aus der Klasse Calendar eingestellt werden.

calendar = new GregorianCalendar();
calendar.setTimeZone( TimeZone.getTimeZone("ECT") );

Hinweis Natürlich verfehlt dies total seinen Zweck, da wir die Zeitzone nicht von Hand setzen sollten. Aber leider funktioniert es auf einigen Computern nicht ohne explizites Setzen.

Abfragen und Setzen von Datumselementen

Das Abfragen und Setzen von Datumselementen des gregorianischen Kalenders erfolgt mit den überladenen Methoden get() und set(). Beide erwarten als ersten Parameter einen Feldbezeichner - eine Konstante aus der Klasse Calendar -, der angibt, auf welches Datum-/Zeit-Feld zugegriffen werden soll. Die get()-Methode liefert den Inhalt des angegebenen Felds und set() schreibt den als zweiten Parameter übergebenen Wert in das Feld. Die nachfolgende Tabelle gibt eine Übersicht der Feldbezeichner und ihrer Wertebereiche.


abstract class java.util.Calendar
implements Serializable, Cloneable


Feldbezeichner
Calendar.*
Minimalwert Maximalwert Erklärung
ERA 0 (BC) 1 (AD) Datum vor oder nach Christus
YEAR 1 5000000 Jahr
MONTH 0 11 Monat (nicht von 1-12!)
DAY_OF_MONTH
alternativ DATE
1 31 Tag
WEEK_OF_YEAR 1 54 Woche
WEEK_OF_MONTH 1 6 Woche des Monats
DAY_OF_YEAR 1 366 Tag des Jahres
DAY_OF_WEEK 1 7 Tag der Woche
](1 = Sonntag, 7 = Samstag)
DAY_OF_WEEK_IN_MONTH -1 6 Tag der Woche im Monat
HOUR 0 12 Stunde von 12
HOUR_OF_DAY 0 23 Stunde von 24
MINUTE 0 59 Minute
SECOND 0 59 Sekunden
MILLISECOND 0 999 Millisekunden
AM_PM 0 1 vor 12, nach 12
ZONE_OFFSET -12*60*60*1000 12*60*60*1000 Zeitzonenabweichung in Millisekunden
DST_OFFSET 0 1*60*60*1000 Sommerzeitabweichung in Millisekunden

Tabelle 10.2 Konstanten aus der Klasse Calendar

Nun können wir mit den Varianten von set() die Felder setzen und mit get() wieder hereinholen. Beachtenswert ist der Anfang der Monate mit 0 und der Anfang der Wochentage mit 1 (SUNDAY), 2 (MONDAY), ..., 7 (SATURDAY) - Konstanten der Klasse Calendar stehen in Klammern. Das ist insbesondere für deutsche Verhältnisse unüblich, denn nach DIN-Norm 1355 beginnt die Woche mit Montag. Die Vereinbarung wurde 1975 mit der ISO getroffen und von den Kalenderherstellern in den folgenden Jahren umgesetzt. Wer »weltliche« Software schreibt, sollte jedoch berücksichtigen, dass die katholische und evangelische Kirche sich dieser Anpassung nicht angeschlossen haben: Sonntag bleibt der erste Tag der Woche. Wer »kirchliche« Software schreibt, sollte das berücksichtigen.


Beispiel Wenn es 19 Uhr ist, dann ist es wieder Zeit für DragonBall Z.

Listing 10.7 ZeitZwischen.java

import java.util.*;
public class ZeitZwischen
{
 public static void main( String args[] )
  {
    if ( new GregorianCalendar().get( Calendar.HOUR_OF_DAY ) == 19 )
      System.out.println( "Zeit für DragonBall Z" );
  }
}

Da die Serie immer genau von 18:00 bis 18:59 läuft, ist es natürlich einfach, auf diese Weise die Abfrage zu formulieren. Bei einer genaueren Angabe müssten wir noch die Minuten prüfen.


abstract class java.util.Calendar
implements Serializable, Cloneable

gp final void set( int field, int value )
Setzt das Feld field mit dem Wert value.
gp final void set( int year, int month, int date )
Setzt die Werte für Jahr, Monat und Tag.
gp final void set( int year, int month, int date, int hour, int minute )
Setzt die Werte für Jahr, Monat, Tag, Stunde und Minute.
gp final void set( int year, int month, int date, int hour,
int minute, int second)
Setzt die Werte für Jahr, Monat, Tag, Stunde, Minute und Sekunde.
gp final int get( int field )
Liefert den Wert für field.

Die Methoden zum gleichzeitigen Setzen von Jahr, Monat und Tag und so weiter sind nur Hilfsfunktionen, und ihre Implementierung ist einfach:

public final void set(int year, int month, int date)
{
  set(YEAR, year);
  set(MONTH, month);
  set(DATE, date);
}

Leider ist es mit einem einfachen set() zum Setzen der Feldwerte oft nicht getan.1 Denn das Problem ist die Konsistenz zwischen den verschiedenen internen Datumsdarstellungen eines Calendar-Exemplars. Hier müssen wir etwas tricksen und durch den Aufruf von setTime(getTime()) die Darstellung in Millisekunden und die verschiedenen anderen Feldwerte abgleichen. Bevor wir also mit get() die Werte wieder abfragen, schreiben wir die Zeile:

calendar.setTime( calendar.getTime() );

Ein gregorianischer Kalender mit eigenen Werten

Wir wollen im nachfolgenden Beispiel ein GregorianCalendar-Objekt erzeugen und mit einer selbst geschriebenen Funktion printCalendar() wichtige Felder ausgeben. Da die Daten nicht bearbeitet werden, handelt es sich um das aktuelle Tagesdatum. Anschließend manipulieren wir mit den set()-Methoden das Objekt und setzen es auf das Geburtsdatum des Autors.

Listing 10.8 DateDemo.java

import java.util.*;
public class DateDemo
{
  public static void main( String args[] )
  {
    GregorianCalendar cal = new GregorianCalendar();
    cal.setTimeZone( TimeZone.getTimeZone("ECT") );
    cal.setTime( cal.getTime() );
    printCalendar( cal );
    cal.set( Calendar.DATE, 12 );
    cal.set( Calendar.MONTH, Calendar.MARCH );
    cal.set( Calendar.YEAR, 1973 );
    printCalendar( cal );
  }
  public static void printCalendar( Calendar cal )
  {
    String dayOfWeek = (new String[]{ "Sonntag", "Montag",
      "Dienstag", "Mittwoch", "Donnertag", "Freitag", "Samstag"})
      [cal.get(Calendar.DAY_OF_WEEK)-1];  // Sonntag = 1
    System.out.println( dayOfWeek + ", " +
                        cal.get(Calendar.DATE) + "." +
                        (cal.get(Calendar.MONTH)+1) + "." +
                        cal.get(Calendar.YEAR) + ", " +
                        cal.get(Calendar.HOUR_OF_DAY) + ":" +
                        out(cal.get(Calendar.MINUTE)) + ":" +
                        out(cal.get(Calendar.SECOND)) + " und " +
                        cal.get(Calendar.MILLISECOND) + " ms" );
    System.out.print( "Es ist die "+
                      cal.get(Calendar.WEEK_OF_YEAR) +
                      ". Woche im Jahr und " );
    System.out.println( cal.get(Calendar.WEEK_OF_MONTH) +
                        ". Woche im Monat\n" );
  }
  public static String out( int i ) {
    return ( i >= 10 ) ? Integer.toString(i) : "0"+i;
  }
}

Die Ausgabe des Programms lautet etwa so:

Dienstag, 19.6.2001, 21:52:13 und 816 ms
Es ist die 25. Woche im Jahr und 3. Woche im Monat
Montag, 12.3.1973, 21:52:13 und 816 ms
Es ist die 11. Woche im Jahr und 3. Woche im Monat

Da die Ausgabe auf diese Art und Weise nicht besonders komfortabel ist, werden wir mit DateFormat eine Klasse kennen lernen, die die Formatierung der Ausgabe vereinfacht.

Wie viele Tage hat der Monat?

Diese Frage lässt sich einfach mit getActualMaximum() klären. Als Parameter wird der Methode ein Feldbezeichner aus der Klasse Calendar übermittelt, dessen Maximum sie dann bestimmt.

Das nachfolgende Programm listet alle Monate des gregorianischen Kalenders auf. Da dieser genau zwölf Monate umfasst, können wir eine feste Schleife programmieren. Für einen Mondkalender sähe dies etwas anders aus. Den Beweis, dass das Programm korrekt funktioniert, sollten wir mit einem Abzählen der Fingerknochen führen.

Listing 10.9 Fingerknochen.java

import java.util.*;
class Fingerknochen
{
  public static void main( String args[] )
  {
    GregorianCalendar cal = new GregorianCalendar();
    System.out.println( cal.getTime() );
    for ( int month = Calendar.JANUARY; month <= Calendar.DECEMBER; month++ )
    {
      cal.set( Calendar.MONTH, month );
      System.out.println( (month+1) + " : " +
        cal.getActualMaximum(Calendar.DAY_OF_MONTH) );
    }
  }
}





1 Dies scheint aber ein Fehler in den bisherigen Implementierungen zu sein.





Copyright (c) Galileo Press GmbH 2004
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press GmbH, Gartenstraße 24, 53229 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de