Inhalt
- Datumsarithmetik
- Java-Klassen-Hierarchie einiger Date- und Calendar-Klassen
- Aufgaben von Date, SimpleDateFormat und GregorianCalendar
- Benutzung von Date, SimpleDateFormat und GregorianCalendar
- Berechnung von Datums-Differenzen
- Konvertierung von und zu LocalDate
- Weiterführende Links
Datumsarithmetik
Datumsarithmetik ist keine triviale Aufgabe. Es müssen viele Bedingungen beachtet werden:
- Eine Minute hat nicht immer 60 Sekunden
(z.B. wenn vom Internationalen Erd-Rotations-Service (IERS) angeordnete Schaltsekunden eingefügt werden,
was z.B. 2006-01-01 zum 23. Mal seit 1972 passierte).
- Ein Tag hat nicht immer 24 Stunden (z.B. bei Sommer-/Winterzeitumstellung).
- Eine Woche hat nicht immer 7 Tage (z.B. die erste Woche im Jahr).
- Ein Jahr hat nicht immer 365 Tage (z.B. in Schaltjahren und bei Kalenderanpassungen).
- Die Berechnung, ob ein Jahr ein Schaltjahr ist, ist nicht immer fehlerfrei (alle 4 Jahre, außer alle 100, wenn nicht durch 400 teilbar).
- Es wurden mehrmals mehrere Tage ausgelassen, um den Kalender an das tatsächliche Sonnenjahr anzupassen (z.B. folgte auf Donnerstag 4. Oktober 1582 als nächster Tag Freitag 15. Oktober 1582).
- Die Datumsgrenze wurde 1884 auf der Standardzeit-Konferenz in Washington auf den 180. Längengrad definiert.
Allerdings weichen einige Pazifikstaaten davon etwas ab und rechnen sich der anderen Zeitzone zu.
Noch schwieriger: Hin und wieder ändern einige Staaten ihre Zugehörigkeit.
Zum Beispiel wechselten Samoa und die Tokelau-Inseln Ende 2011 die Zeitzone und übersprangen dadurch den 30. Dezember 2011.
- Es gibt nicht das Jahr 0. Nach 1 B.C. (vor Christi) folgte 1 A.D. (nach Christi).
- Die Zahl der Tage, ab der diese Tage als erste Woche im Jahr gelten, ist unterschiedlich definiert.
Dadurch kann die KW-Nummerierung unterschiedlich ausfallen (KW = Kalenderwoche).
- Der Wochenanfang ist manchmal mit Sonntag und manchmal mit Montag definiert.
- Es gibt unterschiedliche Behandlungen von zweistelligen Jahreszahlen. Sie können als Jahreszahlen im ersten Jahrhundert A.D. gemeint sein oder es muss 1900 oder bei kleinen Werten vielleicht auch 2000 dazu addiert werden.
- Die Angabe 01/02/03 kann in Deutschland 2003-02-01, in USA 2003-01-02 und in asiatischen Staaten 2001-02-03 bedeuten.
- Die Uhrzeit kann bis 12 AM/PM oder bis 24 h zählen.
- Zeitzonen müssen berücksichtigt werden. Die Bestimmung der Zeitzone eines Ortes ist nicht einfach.
- Es gibt unterschiedliche und sich ändernde lokale Bestimmungen und Anfangs-/Endzeiten zur Sommer-/Winterzeit.
In Europa beginnt die Sommerzeit seit 1980 am letzten Sonntag im März, endete bis 1995 am letzten Sonntag im September und endet seit 1996 am letzten Sonntag im Oktober.
- In Russland wurde 2011 die Zeitumstellung abgeschafft.
- Der Unterschied von Sommer- zur Winterzeit beträgt nicht immer eine Stunde. Zum Beispiel galt 1947 in Deutschland die Hochsommerzeit (MEHSZ) mit zwei Stunden Unterschied.
- Es gibt verschiedene Kalendersysteme (z.B. 1-Jan-2000 (Gregorian) = 23-Tevet-5760 (Hebrew) = 24-Ramadan-1420 (Islamic)).
- Der bei Astronomen beliebte 'Julian Day calendar' (Zyklen von 7980 astronomischen Jahren beginnend ab 1. Jan. 4713 B.C.) ist nicht das Gleiche wie der von Julius Caesar eingeführte und bei einigen Orthodoxen heute noch benutzte 'Julian Calendar' (in Europa von 1. Jan. 45 B.C. bis 4. Okt. 1582 A.D.).
- Die Länder, die sich auf den (von Papst Gregory XIII eingeführten) gregorianischen Kalender umgestellt haben, haben dies zu sehr verschiedenen Zeiten vollzogen, wodurch historische Berechungen erschwert werden (viele europäische Länder 1582, aber z.B. China erst 1949 und andere Länder gar nicht).
- Es gibt Beschränkungen im Wertebereich der Programmiersprachen-Funktionen.
Z.B. in Java akzeptiert Date 290 Millionen Jahre B.C. (vor Christi) bis 290 Millionen Jahre A.D. (nach Christi), aber GregorianCalendar akzeptierte vor JDK 1.2 kein Datum früher als 4713 B.C.
Noch gravierender: 2038-01-19 sind 2^31 Sekunden seit 1970-01-01 vergangen und damit endet der Wertebereich des unter Unix und Windows bei 32-Bit-Programmierung in C üblichen time_t.
Java-Klassen-Hierarchie einiger Date- und Calendar-Klassen
Aufgaben von Date, SimpleDateFormat und GregorianCalendar
Bis JDK 1.0.2 diente die Klasse Date zur Darstellung und Manipulation von Datumswerten.
Ab JDK 1.1 wurde Calendar zur Bearbeitung von Datumswerten eingeführt und fast alle Methoden von Date als Deprecated markiert.
Die wichtigsten Aufgaben der drei wichtigsten Klassen sind:
- Date
Speichern eines Datum-/Zeitwertes (als 64-Bit-Long-Variable) in ms (Millisekunden) seit 1970-01-01 0h UTC (negative Werte sind vor 1970).
- SimpleDateFormat
Formatierte Ein-/Ausgabe von Datums- und Zeitwerten.
- Calendar und GregorianCalendar
Berechnungen mit Datums- und Zeitwerten.
Benutzung von Date, SimpleDateFormat und GregorianCalendar
TimeZone
Der Wert von TimeZone.getDefault() sollte auf der System Property "user.timezone" basieren,
welcher von der JVM gesetzt werden kann.
Aber zumindest in früheren JDK-Versionen war sie häufig nicht gesetzt.
Dann benutzt TimeZone.getDefault() einen eigenen 'Fallback'-Wert, der bis JDK 1.1.3 'PST' (North American Pacific Timezone) war und danach auf 'GMT' (Greenwich Mean Time) geändert wurde.
Ab JDK 1.2 benutzen Date, SimpleDateFormat und Calendar diese Default-Zeitzone.
Vorher war Calendar in SimpleDateFormat auf die erste lokale Zeitzonen-Ressource gesetzt (z.B. in USA auf 'PST').
Diese Unsicherheit kann leicht vermieden werden mit:
myDateFormat.setTimeZone( TimeZone.getDefault() );
import java.util.*;
// Anzeige aller Zeitzonen-IDs:
String[] ssIDs = TimeZone.getAvailableIDs();
for( int i=0; i<ssIDs.length; i++ ) System.out.println( ssIDs[i] );
// Anzeige der aktuellen Default-Zeitzone:
System.out.println( "Default-Zeitzone = " + TimeZone.getDefault().getID() ); // z.B. 'Europe/Berlin'
System.out.println( "user.timezone = " + System.getProperty( "user.timezone" ) ); // z.B. 'Europe/Berlin'
Date
// Aktuelles Datum:
import java.util.*;
Date dt = new Date();
System.out.println( "Date = " + dt ); // z.B. 'Fri Jan 26 19:03:56 GMT+01:00 2001'
System.out.println( "ms = " + dt.getTime() ); // z.B. '980532236731'
SimpleDateFormat
import java.util.*;
import java.text.*;
Date dt = new Date();
// Festlegung des Formats:
SimpleDateFormat df = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss.S" );
df.setTimeZone( TimeZone.getDefault() ); // nicht mehr unbedingt notwendig seit JDK 1.2
// Formatierung zu String:
System.out.println( "Date = " + df.format( dt ) ); // z.B. '2001-01-26 19:03:56.731'
// Ausgabe für andere Zeitzone:
df.setTimeZone( TimeZone.getTimeZone( "Africa/Casablanca" ) );
System.out.println( "Casablanca = " + df.format( dt ) ); // z.B. '2001-01-26 18:03:56.731'
// Einlesen vom String:
dt = df.parse( "2001-02-03 04:05:06.7" );
System.out.println( "parse = " + df.format( dt ) ); // z.B. '2001-02-03 04:05:06.7'
Calendar und GregorianCalendar
GregorianCalendar benutzt immer das gregorianische Kalendersystem, während Calendar auch andere Kalendersysteme benutzen könnte (z.B. Hebrew oder Islamic). Bislang gibt es von Sun allerdings noch keine anderen Implementationen.
Mit Calendar.getInstance() wird ein Kalender-Objekt im lokal gültigen Kalendersystem erzeugt.
GregorianCalendar() dagegen erzeugt stets ein Objekt im gregorianischen Kalendersystem.
Das Lesen und Setzen von Monatsangaben sollte bevorzugt über die definierten Konstanten erfolgen (z.B. 'Calendar.MARCH').
Wird stattdessen der Integer-Wert benutzt, muss beachtet werden, dass der Januar mit 0 und nicht mit 1 beginnt.
Wenn man mit Calendar.set() ein Feld setzt und anschließend mit Calendar.get() ein anderes Feld liest, waren die Felder vor JDK 1.2 nicht unbedingt konsistent (z.B. konnte der Wochentag falsch sein).
In solchen Fällen kann Konsistenz einfach erreicht werden mit:
myCal.setTime( myCal.getTime() );
// Verschiedene Verfahren zur Erzeugung eines Kalender-Objekts:
import java.util.*;
Calendar myCal1 = Calendar.getInstance(); // lokales Kalendersystem mit aktueller Zeit
Calendar myCal2 = new GregorianCalendar(); // GregorianCalendar mit aktueller Zeit
Calendar myCal3 = new GregorianCalendar(1956,Calendar.MARCH,17); // GregorianCalendar mit vorgegebener Zeit
// Setzen von Jahr + Monat + Tag:
myCal2.set( 1956, Calendar.MARCH, 17 ); // ändert so nicht Stunden, Minuten und Sekunden
myCal2.setTime( myCal2.getTime() ); // nicht mehr unbedingt notwendig seit JDK 1.2
// Zeit setzen mit Date:
myCal2.setTime( new Date() );
// Einzelne Felder extrahieren:
int year = myCal2.get( Calendar.YEAR );
int mnth = myCal2.get( Calendar.MONTH ) + 1; // nicht vergessen die 1 zu addieren
int date = myCal2.get( Calendar.DATE );
System.out.println( date + "." + mnth + "." + year ); // für Formatierung ist SimpleDateFormat besser
// Calendar ist gut geeignet, um Berechnungen durchzuführen,
// z.B. um den um ein Jahr erhöhten Zeitwert zu berechnen:
myCal2.add( Calendar.YEAR, 1 );
// Umwandlung von Calendar in Date:
Date myDate = myCal2.getTime();
System.out.println( myDate );
Berechnung von Datums-Differenzen
Die Berechnung von Datums-Differenzen ist nicht trivial, wie schon oben unter Datumsarithmetik verdeutlicht wurde.
Das Java JDK bietet dafür bislang keine Methode.
Oft funktioniert die im Folgenden gezeigte vereinfachte Variante.
Aber zum Beispiel bei historischen Berechnungen, verschiedenen Zeitzonen oder wenn der eine Zeitpunkt Sommerzeit und der andere Winterzeit hat, kann es zu Fehlern kommen.
Dann muss eine Transformation in UTC (Coordinated Universal Time) oder Julian Days vorgeschaltet werden.
In der Literatur werden dazu verschiedene und teilweise recht aufwändige Prozeduren vorgestellt.
Die beiden Zeitpunkte werden hier mit Jahr + Monat + Tag definiert, sie könnten aber genauso gut per Date-Objekt definiert werden (s.o.).
Calendar cal_1 = new GregorianCalendar();
Calendar cal_2 = new GregorianCalendar();
cal_1.set( 1997, Calendar.MARCH, 1, 0, 0, 0 ); // erster Zeitpunkt
cal_2.set( 1998, Calendar.APRIL, 2, 0, 0, 0 ); // zweiter Zeitpunkt
long time = cal_2.getTime().getTime() - cal_1.getTime().getTime(); // Differenz in ms
long days = Math.round( (double)time / (24. * 60.*60.*1000.) ); // Differenz in Tagen
System.out.println( "Zeit-Differenz in Tagen: " + days );
Konvertierung von und zu LocalDate
Seit Java 8 gibt es ein erheblich verbessertes Time und Date API.
Eine der neuen Klassen ist java.time.localDateTime.
Die Konvertierung von Date nach localDateTime und umgekehrt kann folgendermaßen erfolgen:
// Date zu LocalDateTime:
LocalDateTime localDateTime = LocalDateTime.ofInstant( Instant.ofEpochMilli( date.getTime() ), ZoneId.systemDefault() );
// LocalDateTime zu Date:
Date date = Date.from( localDateTime.atZone( ZoneId.systemDefault() ).toInstant() );
// LocalDateTime zu LocalDate:
LocalDate localDate = localDateTime.toLocalDate();
// LocalDate zu Date:
Date date = Date.from( localDate.atStartOfDay().atZone( ZoneId.systemDefault() ).toInstant() );
Weiterführende Links
- PTB: MEZ, MESZ, UTC, Gregorianischer Kalender, Sommerzeit, Zeitzonen: http://www.ptb.de/deutsch/org/4/43/432/dars.htm
- W3C: ISO 8601, YYYY-MM-DDThh:mm:ssTZD: http://www.w3.org/TR/NOTE-datetime
- NCITS: Dates, Times and Durations: http://www.ncits.org/tc_home/j4htm/m219/99-0217.htm
- NPL: Gregorian / Julian Calendar / Julian date / MJD, GMT / UT / UTC: http://www.npl.co.uk/npl/ctm/time_scales.html
- Twin Sun: Zeitzonen, Sommerzeit: http://www.twinsun.com/tz/tz-link.htm
- RGO: GMT, UT, UTC: http://ecco.bsee.swin.edu.au/chronos/GMT-explained.html
- Stockton: Critical and Significant Dates: http://www.merlyn.demon.co.uk/critdate.htm
- 12Ghosts: Calendar Systems, atomic clock: http://12ghosts.com/ghosts/sync.htm
- BCN: Umrechnung zwischen Julian and Gregorian Day Numbers: http://bcn.boulder.co.us/y2k/y2kbcalc.htm
- WebExhibits: Kalendersysteme und Umrechnungen: http://webexhibits.org/calendars/calendar.html
- Horstmann/Cornell, Core Java: toJulian() / fromJulian(): http://wimp.nsm.uh.edu/ytanGraph/Dayjava.html, http://www.cs.olemiss.edu/~hcc/csci581/notes/dataAbstraction.html#dayADT, http://www.awra.org/proceedings/www99/w16/UTCDate.htm
- Arbeitstage Bayern und NRW: http://www.arbeitswelt.de/Service/start-Service.htm
Weitere Themen: andere TechDocs
© 1998-2007 Torsten Horn, Aachen