Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger

 << zurück
Java ist auch eine Insel von Christian Ullenboom
Programmieren für die Java 2-Plattform in der Version 5
Java ist auch eine Insel

Java ist auch eine Insel
5., akt. und erw. Auflage
1454 S., mit CD, 49,90 Euro
Galileo Computing
ISBN 3-89842-747-1
gp Kapitel 10 Raum und Zeit
  gp 10.1 Greenwich Mean Time (GMT)
  gp 10.2 Wichtige Datum-Klassen im Überblick
  gp 10.3 Sprachen der Länder
    gp 10.3.1 Sprachen und Regionen über Locale-Objekte
  gp 10.4 Übersetzung durch ResourceBundle-Objekte
  gp 10.5 Zeitzonen
    gp 10.5.1 Zeitzonen durch die Klasse TimeZone repräsentieren
  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.7.3 Ostertage
    gp 10.7.4 Abfragen und Setzen von Datumselementen
  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 GregorianCalendadowntop

Ein Kalender unterteilt die Zeit in Einheiten wie Jahr, Monat, Tag. Der bekannteste Kalender ist der gregorianische Kalender, den Papst Gregor XIII. im Jahre 1582 einführte. Vor seiner Einführung war der julianische Kalender populär, der auf Julius Cäsar zurückging – daher auch der Name. Er geht auf das Jahr 46 vor unserer Zeitrechung zurück. Der gregorianische und der julianische Kalender sind Sonnenkalender, die den Lauf der Erde um die Sonne als Basis für die Zeiteinteilung nutzen; der Mond spielt keine Rolle. Daneben gibt es Mondkalender wie den islamische Kalender und die Lunisolarkalender, die Sonne und Mond miteinander verbinden. Zu diesem Typus gehören der chinesische, der griechische und der jüdische Kalender.

Mit Exemplaren vom Typ Calendar ist es möglich, Datum und Uhrzeit in den einzelnen Komponenten wie Jahr, Monat, Tag, Minute, Sekunde zu setzen und zu erfragen. Da es unterschiedliche Kalendertypen gibt, ist Calendar eine abstrakte Basisklasse, und Unterklassen bestimmen, wie konkret eine Abfrage oder Veränderung für ein bestimmtes Kalendersystem aussehen muss. Bisher bringt die Java-Bibliothek mit der Unterklasse GregorianCalendar nur eine konkrete Implementierung mit, deren Exemplare Daten und Zeitpunkte gemäß dem gregorianischen Kalender verkörpern. IBM hat mit International Components for Unicode for Java (ICU4J) unter http://www-306.ibm.com/software/globalization/icu/ weitere Klassen wie ChineseCalendar, BuddhistCalendar, JapaneseCalendar, HebrewCalendar, IslamicCalendar freigegeben. Hier findet sich auch einiges zum Thema Ostertage.


Galileo Computing

10.7.1 Die abstrakte Klasse Calendar  downtop

Die Klasse Calendar besitzt zum einen Anfrage- und Modifikationsfunktionen für konkrete Exemplare und zum anderen statische Fabrikfunktionen. Eine einfache Klassen-Methode ist getInstance(), um ein benutzbares Objekt zu bekommen.


abstract class java.util.  Calendar
  implements Serializable, Cloneable, Comparable<Calendar>

gp  static Calendar getInstance() Liefert einen Standard-Calendar mit der Standard-Zeitzone und Standard-Lokalisierung zurück.

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 mit diesen Orten zurück. Das Feld enthält mindestens Locale.US.

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


Galileo Computing

10.7.2 Der gregorianische Kalendedowntop

Die abstrakte Klasse Calendar wird durch die Klasse GregorianCalendar erweitert – in Java 6 ist eine weitere Klasse für einen Japanischen Kalender geplant. Der GregorianCalendar deckt vom Jahr 4716 vor unserer Zeitrechnung bis zu den nächsten Millionen Jahren 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 sind die lokale Zeitzone und die aktuelle Lokalisierung. Ist eines der Argumente 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. Die Konstanten sind keine typsicheren Enums, bieten aber den Vorteil als int einfach mit ihnen zählen zu können.


Hinweis   Zum Aufbau von Calendar-Objekten gibt es nun zwei Möglichkeiten:
Calendar c = Calendar.getInstance();
und
Calendar c = new GregorianCalendar();
Die erste Variante ist besonders in internationalisierter Software zu bevorzugen, da es einige Länder gibt, die nicht nach dem gregorianischen Kalender arbeiten. Calendar c = Calendar.getInstance( new Locale("ja", "JP", "JP") );

Früher Fehler: Standard-Konstruktor setzt falsche Zeitzone

Leider liefert der Standard-Konstruktor von GregorianCalendar nicht immer die erwarteten Ergebnisse. Zur Klärung sehen wir uns die Implementierung genauer an:

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

Wird der gregorianische Kalender ohne Zeitzonenangabe konstruiert, so ruft die Klasse TimeZone die statische Methode getDefault() auf. In der Implementierung von getDefault() wird in den Systemeigenschaften nach user.timezone gefragt, und falls dieser Eintrag dann nicht existiert, wird die Zeitzone GMT angenommen. In Deutschland haben wir jedoch Central European Time, kurz CET, und nicht GMT! Ist die Zeitzone auf Rechnern nicht auf CET gestellt, 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("CET") );

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.


Galileo Computing

10.7.3 Ostertage  downtop

In vielen Geschäftsprogrammen gibt es Fragen nach dem Ostersonntag, da er Bezugspunkt für viele Feiertage ist. Aschermittwoch (Beginn des 40-tägigen Fastens und trauriger Tag für die Kölner) ist 46 Tage vor Ostersonntag, Christi Himmelfahrt 39 Tage nach Ostersonntag und immer Donnerstag, Pfingstsonntag 49 Tage und Fronleichnam 60 Tage nach Ostersonntag. In manchen Gegenden werden drei Tage vor Christi Himmelfahrt Bitttage gefeiert. Der vorletzte Sonntag vor Ostern heißt Passionssonntag, der Sonntag vor Ostern Palmsonntag.

Zur Berechnung des Ostersonntags gibt es unzählige Algorithmen. Eine Formel wurde 1876 in Butchers »Ecclesiastical Calendar« veröffentlicht. Er arbeitet für Jahre ab 1582 im gregorianischen Kalender korrekt, berücksichtigt aber keine julianischen Zeiten und liefert den Ostersonntag zwischen dem 22. März und 25. April.

Listing 10.6   com/javatutor/insel/easter/Easter.java

package com.javatutor.insel.easter;
import java.util.GregorianCalendar;
public class Easter
{
  /**
   * Berechnet Ostersonntag für den gregorianisch Kalender, also für Jahre > 1583.
   * Die Rückgabe enthält das gleiche Jahr und als Monat entweder März oder April
   * sowie den korrekten Tag.
   *
   * @param year > 1583
   * @return Ostersonntag.
   */
   public static GregorianCalendar easterSunday( int year )
   {
    int i = year % 19;
    int j = year / 100;
    int k = year % 100;
    int l = (19 * i + j – (j / 4) – ((j – ((j + 8) / 25) + 1) / 3) + 15) % 30;
    int m = (32 + 2 * (j % 4) + 2 * (k / 4) – l – (k % 4)) % 7;
    int n = l + m – 7 * ((i + 11 * l + 22 * m) / 451) + 114;
    int month = n / 31;
    int day   = (n % 31) + 1;
    return  new GregorianCalendar( yearmonth – 1day );
  }
}

Ein Test soll für das aktuelle Jahr und die folgenden Jahre den Ostersonntag ausgeben.

Listing 10.7   EasterDemo.java

import java.text.DateFormat;
import java.util.Calendar;
import static com.javatutor.insel.easter.Easter.easterSunday;
import static java.lang.System.out;
public class EasterDemo
{
  public static void main( String[] args )
  {
    Calendar cal = easterSunday( Calendar.getInstance().get(Calendar.YEAR) );
    DateFormat df = DateFormat.getDateInstance( DateFormat.FULL );
    String s = df.format( cal.getTime() );
    out.println( s );                                       // Sonntag, 27. März 2005
    out.println( df.format(easterSunday(2006).getTime()));  // Sonntag, 16. April 2006
    out.println( df.format(easterSunday(2007).getTime()));  // Sonntag, 8. April 2007
    out.println( df.format(easterSunday(2008).getTime()));  // Sonntag, 23. März 20086
    out.println( df.format(easterSunday(2009).getTime()));  // Sonntag, 12. April 2009
    out.println( df.format(easterSunday(2010).getTime()));  // Sonntag, 4. April 2010
    out.println( df.format(easterSunday(2011).getTime()));  // Sonntag, 24. April 2011
    out.println( df.format(easterSunday(2012).getTime()));  // Sonntag, 8. April 2012
    out.println( df.format(easterSunday(2013).getTime()));  // Sonntag, 31. März 2013
  }
}

Galileo Computing

10.7.4 Abfragen und Setzen von Datumselementen  toptop

Da java.util.Date-Objekte zwar auf den ersten Blick Konstruktoren anbieten, die Jahr, Monat, Tag entgegennehmen, diese aber veraltet sind, können wir den Blick auf GregorianCalendar lenken. Um von diesem Calendar nun wieder die Anzahl der vergangenen Millisekunden seit dem 1.1.1970 abzufragen, dient getTimeInMillis(). (Eine ähnliche Methode hat auch Date, nur heißt sie dort getTime().)


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) / (1000*60*60*24);

Eine Methode getTime() hat Calendar auch. Nur liefert die finale Funktion uns ein java.util.Date-Objekt und kein long.


abstract class java.util.  Calendar
  implements Serializable, Cloneable, Comparable<Calendar>

gp  final long getTimeInMillis() Liefert die seit der Epoche (January 1, 1970 00:00:00.000 GMT, Gregorian) vergangene Zeit in die Millisekunden.
gp  final Date getTime() Liefert ein Date-Objekt zu diesem Calendar.

Zugriff auf Felder über Feldbezeichner

Das Abfragen und Setzen von Datumselementen des gregorianischen Kalenders erfolgt mit den überladenen Methoden get() und set(). Beide erwarten als erstes Argument 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 zweites Argument übergebenen Wert in das Feld. Die nachfolgende Tabelle gibt eine Übersicht der Feldbezeichner und ihrer Wertebereiche.


abstract class java.util.  Calendar
  implements Serializable, Cloneable, Comparable<Calendar>


Tabelle 10.2   Konstanten aus der Klasse Calendar

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

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.8   ZeitZwischen.java, main()

if ( Calendar.getInstance().get( Calendar.HOUR_OF_DAY ) == 19 )
  System.out.println( "Zeit für DragonBall Z" );
Da (angenommen) die Serie immer genau von 19:00 bis 19: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, Comparable<Calendar>

gp  final int get( int field ) Liefert den Wert für field.
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.

Beispiel   In früheren Java-Versionen gab es schon mal Probleme mit dem Setzen einzelner Werte. Dann hat der Aufruf von calendar.setTime(calendar.getTime()); die verschiedenen internen Feldwerte wieder neu abgeglichen.

Ein gregorianischer Kalender mit eigenen Werten

Wir wollen im nachfolgenden Beispiel ein Calendar-Objekt erzeugen und mit einer selbst geschriebenen Funktion printCalendar() wichtige Felder ausgeben. Weil 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.Calendar;
public class DateDemo
{
  public static void main( String[] args )
  {
    Calendar cal = Calendar.getInstance();
    printCalendar( cal );
    cal.set( Calendar.DATE12 );
    cal.set( Calendar.MONTHCalendar.MARCH );
    cal.set( Calendar.YEAR1973 );
    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.printf( "%s, %s.%s.%s, %02d:%02d:%02d und %d ms%n",
                       dayOfWeek,
                       cal.get(Calendar.DATE),
                       cal.get(Calendar.MONTH) + 1,
                       cal.get(Calendar.YEAR),
                       cal.get(Calendar.HOUR_OF_DAY),
                       cal.get(Calendar.MINUTE),
                       cal.get(Calendar.SECOND),
                       cal.get(Calendar.MILLISECOND));
    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" );
  }
}

Die Ausgabe des Programms lautet in etwa so:

Donnertag7.7.200514:28:03 und 133 ms
Es ist die 27Woche im Jahr und 1Woche im Monat
Montag12.3.197314:28:03 und 133 ms
Es ist die 11Woche im Jahr und 3Woche 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. (Wir hätten im Programm schon ein Problem, wenn die Woche mehr als sieben Tage hätte.) Das Beispiel soll ausschließlich die Nutzung der Feldbezeichner zeigen. Und da wir schon beim Thema »Formatieren« sind: Auch mit Formatstrings aus Formatter, die wir mit printf() oder String.format() nutzen, sind Datumsausgaben möglich.

Werte relativ setzen

Neben der Möglichkeit, die Werte entweder über den Konstruktor oder über set() absolut zu setzen, sind auch relative Veränderungen möglich. Dazu wird die add()-Methode eingesetzt, die wie set() als erstes Argument einen Feldbezeichner bekommt und als zweites die Verschiebung. Da es keine sub()-Methoden gibt, können die Werte auch negativ sein.


Beispiel   Wo waren wir heute vor einem Jahr?

Listing 10.10   NowMinusOne.java, main()

Calendar c = Calendar.getInstance();
System.out.println( DateFormat.getInstance().format(c.getTime()) ); // 07.07.05 15:02
  c.add( Calendar.YEAR, –1 );
  System.out.println( DateFormat.getInstance().format(c.getTime()) ); // 07.07.04 15:02

Eine weitere Methode roll() ändert keine folgenden Felder, was add() macht, wenn etwa auf dem dreißigsten eines Monates zehn Tage addiert werden.

Wie viele Tage hat der Monat?

Diese Frage lässt sich einfach mit getActualMaximum() klären. Als Argument 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.11   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.MONTHmonth );
      System.out.println( (month+1) + " : " +
        cal.  getActualMaximum  (Calendar.DAY_OF_MONTH) );
    }
  }
}



1  Viele wissen es nicht mehr: Da ist Jesus auferstanden.

 << zurück




Copyright © Galileo Press GmbH 2005
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, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de