Galileo Computing < openbook >
Galileo Computing - Programming the Net
Galileo Computing - Programming the Net


Java ist auch eine Insel (2. Aufl.) von Christian Ullenboom
Programmieren für die Java 2-Plattform in der Version 1.4
Java ist auch eine Insel (2. Auflage)
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.6.2 Zeitmessung und Profiling
  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 GregorianCalendar  downtop

Calendar ist eine abstrakte Klasse, die zur Konvertierung zwischen verschiedenen Datum-Formaten eingesetzt wird. Ü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 Calendar  downtop

Die Klasse Calendar besitzt nur wenige statische Funktionen, die sofort genutzt werden können. Eine Klassen- und Fabrik-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

Neben der parameterlosen Variante von getInstance() bestehen 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 Kalender  toptop

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 und vier 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 ist.
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. Da diese Einstellung bei den meisten Rechnern 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 ohne explizites Setzen auf einigen Computern nicht.

Abfragen und Setzen von Datumselementen

Das Abfragen und Setzen von Datumselementen des gregorianischen Kalenders erfolgt mit den überladenen Methoden set() und get(). 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 Wertbereiche.

abstract class java.util.Calendar
implements Serializable, Cloneable

Tabelle 10.2   Konstanten aus der Klasse Calendar
Feldbezeichner
Calendar.*
Minimalwert Maximalwert Erklärung

ERA

0 (BC) 1 (AD) Datum vor oder nach Chr.

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

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.

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

Listing 10.8   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 genauer Angabe müssten wie 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. 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 folgenden 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.9   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 folgende Programm listet alle Monate des gregorianischen Kalenders auf. Da dieser genau 12 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.10   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 © Galileo Press GmbH 2003
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