10.8 Formatieren der Datumsangaben
 
Nachdem wir nun eine Unterklasse von Calendar, nämlich GregorianCalendar, dazu benutzt haben, Datumswerte zu verwalten, wollen wir nun untersuchen, wie eine Formatierungsklasse dazu landestypische Ausgaben erzeugt.
10.8.1 Mit DateFormat und SimpleDateFormat formatieren
 
Bevor wir mit den Methoden der Klasse Calendar/GregorianCalendar weitermachen, wollen wir eine neue Klasse kennen lernen, die die Ausgabe und das Einlesen der Datum-Felder übernimmt. Es handelt sich dabei um die Klasse DateFormat. Da DateFormat abstrakt ist, wird erst eine implementierende Klasse einsatzbereit sein. Natürlich liegt eine solche Klasse auch vor: SimpleDateFormat. Die Klasse bietet reichhaltig Methoden zum Zerlegen von Datum-Zeichenketten und Methoden zur Ausgabe unter verschiedenen Sprachen und Formatierungen an.
Beispiel Ausgabe des Datums ohne zusätzliche Formatierungsanweisungen.
Listing 10.11
DateFormater.java
import java.util.*;
import java.text.*;
public class DateFormater
{
public static void main( String args[] )
{
Calendar cal;
cal = new GregorianCalendar( TimeZone.getTimeZone("ECT") );
cal.setTime( cal.getTime() );
|
SimpleDateFormat formater = new SimpleDateFormat();
System.out.println( formater.format( cal.getTime() ) );
}
}
|
Um ein Datum-Objekt zu formatieren, müssen wir zunächst ein Exemplar von Simple DateFormat erzeugen. Dieses bekommt dann eventuell Formatierungsanweisungen (über eine andere Methode oder über einen weiteren Konstruktor) und formatiert dann mit der format()-Funktion das Datum. Die format()-Funktion gibt einen String zurück.
Wir erzeugen uns ein Calendar-Objekt, um die genaue Zeit zu haben beziehungsweise um auch andere Tage setzen zu können, und erzeugen dann den Formatierer mit dem Standard-Konstruktor. Mit der getTime()-Funktion holen wir aus dem Calendar ein Date-Exemplar heraus, denn format() akzeptiert nur Date-Objekte als Parameter. Die Ausgabe des Programms liefert nicht ganz Jahr 20001
fest:
11.04.00 00:15
class java.text.SimpleDateFormat
extends DateFormat
|
|
SimpleDateFormat()
Erzeugt ein neues SimpleDateFormat-Objekt in der eingestellten Sprache. |
abstract class java.text.DateFormat
extends Format implements Cloneable
|
|
final String format( Date date )
Formatiert das Datum in einen Datum/Zeit-String. |
|
static final DateFormat getDateInstance()
static final DateFormat getTimeInstance()
static final DateFormat getDateTimeInstance()
Liefert einen Datum-/Zeit-Formatierer mit dem vorgegebenen Stil aus der Standardumgebung. |
|
static final DateFormat getDateInstance( int dateStyle )
static final DateFormat getTimeInstance( int style )
Liefert einen Datum-/Zeit-Formatierer mit dem Stil style und der Standardsprache. |
|
static final DateFormat getDateInstance( int style, Locale aLocale )
static final DateFormat getTimeInstance( int style, Locale aLocale )
Liefert einen Datum-/Zeit-Formatierer mit dem Stil style und der Sprache aLocale. |
|
static final DateFormat getDateTimeInstance( int dateStyle, int timeStyle )
Gibt einen Datum-/Zeit-Formatierer für die gesetzte Sprache im angegebenen Formatierungsstil zurück. |
|
static final DateFormat getDateTimeInstance( int dateStyle, int timeStyle,
Locale aLocale )
Gibt einen Datum-/Zeit-Formatierer für die Sprache aLocale im angegebenen Formatierungsstil zurück. |
Vorgefertigte Formatierungen
Wir haben im vorherigen Beispiel gesehen, dass das Ausgabeformat auf Monat, Tag, Jahr, Leerzeichen, Stunde, Minute festgelegt ist. Nun bietet die DateFormat-Klasse aber auch die statischen Methoden getDateInstance(), getTimeInstance() und getDateTimeInstance() mit den Parametern DateFormat.SHORT, DateFormat.MEDIUM, DateFormat.LONG oder DateFormat. FULL an, die die Zeit beziehungsweise das Datum auf vier Arten formatieren:
Tabelle 10.3
Konstanten aus DateFormat und ihre Wirkung
Konstante
|
Beispiel für Datum
|
Beispiel für Zeit
|
SHORT
|
29.9.97
|
21:51
|
MEDIUM
|
29.9.1997
|
21:54:46
|
LONG
|
19. September 1997
|
21:53:20 GMT+02:00
|
FULL
|
Montag, 29. September 1997
|
21.53 Uhr GMT+02:00
|
Beispiel mit Sprachen
Um ein Datum oder eine Zeitangabe für unterschiedliche Sprachen aufzubereiten, können wir mit der getDateInstance()-Funktion auch ein DateFormat-Objekt für eine bestimmte Sprachumgebung bekommen. Unter Locale sind für einige Sprachen Konstanten vordefiniert: ENGLISH, FRENCH, GERMAN, ITALIAN, JAPANESE, KOREAN, CHINESE, SIMPLIFIED_CHINESE und TRADITIONAL_CHINESE. Nicht alle Sprachen erzeugen gültige Datumsausgaben. Um für das französische Datum und die Zeit einen Formatierer zu bekommen, schreiben wir einfach:
DateFormat df = DateFormat.getDateInstance( Locale.FRANCE );
Nun wollen wir das individuelle Formatieren in einem Beispiel zusammenbauen. Das Programm erzeugt die Ausgabe für Deutschland und anschließend für Italien.
Listing 10.12
SimpleDateFormater.java
import java.util.*;
import java.text.*;
public class SimpleDateFormater
{
public static void main( String args[] )
{
Calendar cal;
cal = new GregorianCalendar( TimeZone.getTimeZone("ECT") );
cal.setTime( cal.getTime() );
DateFormat formater;
formater = DateFormat.getDateTimeInstance(
DateFormat.FULL, DateFormat.MEDIUM );
System.out.println( formater.format( cal.getTime() ) );
formater = DateFormat.getDateTimeInstance(
DateFormat.FULL, DateFormat.MEDIUM, Locale.ITALY );
System.out.println( formater.format( cal.getTime() ) );
}
}
Die Ausgabe ist folgende:
Donnerstag, 8. Februar 2001 14:23:02
giovedì 8 febbraio 2001 14.23.02
Eine noch individuellere Ausgabe
Um das Ausgabeformat individueller anzupassen, kann ein Formatierungs-String die Ausgabe anpassen. Diese Formatierungsanweisung, in der alle ASCII-Zeichen eine bestimmte Bedeutung haben, wird entweder dem Konstruktor der Klasse SimpleDateFormat übergeben oder kann nachträglich mit der applyPattern()-Methode geändert werden. Nachfolgend die Tabelle der erlaubten Symbole mit ihren Sonderbedeutungen. Mehrfach wiederholte Zeichen werden, wenn möglich, durch die Langform der jeweiligen Angabe ersetzt. Alle diese Symbole sind nicht an eine bestimmte Sprache gebunden.
Tabelle 10.4
Symbole im Formatierungs-String zur Steuerung der Ausgabe
Symbol
|
Bedeutung
|
Präsentation
|
Beispiel
|
G
|
Ära
|
Text
|
AD
|
y
|
Jahr
|
Nummer
|
1998
|
M
|
Monat im Jahr
|
Nummer
|
7
|
MM
|
Monat im Jahr mit 0
|
Nummer
|
07
|
MMM
|
Monat im Jahr kurz
|
Text
|
Sep
|
MMMM
|
Monat im Jahr lang
|
Text
|
September
|
d
|
Tag im Monat
|
Nummer
|
26
|
h
|
Stunde (1-12)
|
Nummer
|
9
|
H
|
Stunde am Tag (0-23)
|
Nummer
|
0
|
m
|
Minute der Stunde
|
Nummer
|
13
|
s
|
Sekunde der Minute
|
Nummer
|
22
|
S
|
Millisekunde
|
Nummer
|
257
|
E
|
Tag der Woche kurz
|
Text
|
Mi
|
EEEE
|
Tag der Woche lang
|
Text
|
Mittwoch
|
D
|
Tag im Jahr
|
Nummer
|
304
|
F
|
Tag der Woche im Monat
|
Nummer
|
3
|
w
|
Woche im Jahr
|
Nummer
|
12
|
W
|
Woche im Monat
|
Nummer
|
3
|
a
|
am- und pm-Text
|
Text
|
AM
|
k
|
Stunde am Tag (1-24)
|
Nummer
|
24
|
K
|
Stunde (0-11)
|
Nummer
|
0
|
z
|
Zeitzone
|
Text
|
GMT+02:00
|
'
|
Zeichen für Text
|
Trennzeichen
|
Hallo Welt
|
''
|
einzelnes Hochkomma
|
Literal
|
’
|
class java.text.SimpleDateFormat
extends DateFormat
|
|
SimpleDateFormat()
Erzeugt ein neues SimpleDateFormat-Objekt in der eingestellten Sprache. |
|
SimpleDateFormat( String pattern )
Erzeugt ein SimpleDateFormat-Objekt mit dem vorgegebenen Formatierungs-String in der voreingestellten Sprache. |
|
SimpleDateFormat( String pattern, Locale locale )
Erzeugt ein SimpleDateFormat-Objekt mit dem vorgegebenen Formatierungs-String in der Sprache loc. |
|
SimpleDateFormat( String pattern, DateFormatSymbols formatSymbols )
Erzeugt ein SimpleDateFormat-Objekt mit dem vorgegebenen Formatierungs-String und einem Objekt formatSymbols, welches die formatierungstypischen Informationen sammelt. |
|
void applyPattern( String pattern )
Setze den Formatierungs-String. |
Beispiel Wir setzen einen eigenen Formatierungs-String.
DateFormat fmt = new SimpleDateFormat( "EE', den' dd.MM.yy 'um' hh:mm:ss" );
|
Setzen wir dies zu einem Beispiel für unseren Sprachraum zusammen:
Listing 10.13
ComplexDateFormater.java
import java.util.*;
import java.text.*;
public class ComplexDateFormater
{
public static void main( String args[] )
{
SimpleDateFormat fmt;
fmt = new SimpleDateFormat();
fmt.applyPattern("EEEE', 'dd. MMMM yyyy 'um' hh:mm:ss");
Calendar cal;
cal = new GregorianCalendar();
System.out.println( fmt.format( cal.getTime() ) );
}
}
Wir erwarten eine Ausgabe der Form:
Sonntag, 04. März 2001 um 08:46:54
Verschreiben wir uns aus Versehen im Formatierungs-String und ein Zeichen taucht nicht unter den erlaubten auf, dann ist uns eine Laufzeit-Exception sicher. So führt ein falsch gesetztes »Y« (fälschlich für Year) zu der Meldung:
java.lang.IllegalArgumentException:Illegal pattern character'Y'
Einige Formatierungs-Strings für den deutschen Raum:
Tabelle 10.5
Symbole im Formatierungs-String zur Steuerung der Ausgabe
Formatierungs-String
|
Ergebnis
|
Yyyy.MM.dd G 'at' hh:mm:ss z
|
2001.03.12 n. Chr. at 03:36:44 GMT+01:00
|
EEE, MMM d, ''yy
|
Mo, Mrz 12, ’01
|
h:mm a
|
3:37 PM
|
hh 'o''clock' a, zzzz
|
03 o’clock PM, GMT+01:00
|
K:mm a, z
|
3:38 PM, GMT+01:00
|
yyyyy. MMMMM. dd GGG hh:mm aaa
|
2001. März. 12 n. Chr. 03:38 PM
|
Der voreingestellte Formatierungs-String der Sprachen
Mit der Methode toPattern() von SimpleDateFormat können wir uns den Formatierungs-String, nach dem das Datum und die Zeit formatiert werden, ausgeben lassen:
SimpleDateFormat formater = (SimpleDateFormat) DateFormat.getDateInstance(
DateFormat.LONG );
System.out.println( formater.format( cal.getTime() ) +
" mit dem Pattern " +
formater.toPattern() +
"\nLocalized: " +
formater.toLocalizedPattern() );
Die Methode toLocalizedPattern() gibt einen Formatierungs-String in der aktuellen Sprache zurück.
Beispiel Für unser oben erzeugtes Standard-Datum ergeben sich für das Pattern und das übersetzte Pattern folgende Ausgaben:
30. September 1997 mit dem Pattern d. MMMM yyyy
Localized: t. MMMM uuuu
|
Wir sehen: Der übersetzte String ist als neuer Formatierungs-String ungültig, denn weder »t« noch »u« sind erlaubte Formatierungszeichen. Allerdings können wir mit der Methode applyLocalizedPattern(String) diesen Formatierungs-String nutzen, denn es gibt zusätzlich auch eine lokalisierte Version der Formatiersymbole. Also können wir auch für Deutschland einen Formatierungs-String mit Symbolen anwenden, der von der übersprachlichen Version abweicht. Folgendes ist denkbar:
format.applyLocalizedPattern( "t. MMMM uu" );
Sie sollten in der Praxis nicht benutzt werden.
class java.text.SimpleDateFormat
extends DateFormat
|
|
void applyLocalizedPattern( String pattern )
Setzt den Formatierungs-String. Eine Besonderheit ist die Anpassung der Formatierungssymbole, also zum Beispiel. uuuu statt yyyy für Jahr. |
|
String toPattern()
Liefert den Formatierungs-String. |
|
String toLocalizedPattern()
Liefert den übersetzten Formatierungs-String. Substituiert einige Formatierungssymbole. |
10.8.2 Parsen von Datumswerten
 
Mit der Klasse DateFormat können wir auch Strings zerlegen, die ein Datum darstellen. Dazu bietet die Klasse zwei Varianten von parse() an, die ein Datum-Objekt zurückliefern. So entspricht der Zeit-String »07/10/96 4:5 PM, PDT« einem Datum, das Date(837039928046) gleichkommt.
Beispiel Parse ein Dataum, welches das Format »Jahr-Monat-Tag« hat, mit Hilfe der Klasse SimpleDateFormat.
SimpleDateFormat format = new SimpleDateFormat( "yyyy-MM-dd" );
Date date = format.parse( "2002-03-12" );
|
Leider ist der Funktionsaufruf von parse("Das Datum") mit dem DateFormater, der als Ergebnis von getDateInstance(Locale.GERMANY) zurückgegeben wird, nicht richtig – obwohl die HTML-Dokumentation dies suggeriert. Es gibt nämlich keine getDateInstance()-Methode, die nur ein Locale nimmt. Somit müssen wir eine nicht lokale Variante wählen, zum Beispiel durch die parameterlose Funktion getDateInstance(). Dann können wir von dem DateFormater die Methode parse(String) aufrufen. Diese muss aber in einen try- und catch-Block gesetzt werden.
Beispiel Fehlerfall bei parse() abfangen.
DateFormat formater = DateFormat.getDateTimeInstance();
try
{
Date date = formater.parse( "1.10.1997 11:54:56" );
} catch ( ParseException e ) { ... }
|
Jetzt haben wir ein Datum in ein Date-Objekt umgewandelt. Es beginnt aber wieder das Problem, dass Date nicht auf ein spezielles Land und auf eine Zeitzone angepasst ist, sondern die GMT in Englisch repräsentiert. Wandeln wir das Datum mit der toString() in ein String-Objekt um, so erhalten wir den fehlerhaften String »Wed Oct 01 09:54:56 GMT+00:00 1997«.
Um nun wieder eine lokalisierte Version zu schaffen, bemühen wir wieder das Objekt formater, das nach den gleichen Regeln der Umwandlung von String nach Datum aus dem Datum wieder einen String erzeugt.
formater = DateFormat.getDateTimeInstance( DateFormat.LONG, DateFormat.LONG );
System.out.println( formater2.format( date ) );
abstract class java.text.DateFormat
extends Format
implements Cloneable, serializable
|
|
Date parse( String source ) throws ParseException
Zerlegt einen Datum- oder einen Zeit-String. |
|
abstract Date parse( String source, ParsePosition ) throws ParseException
Zerlegt einen Datum- oder einen Zeit-String und beginnt beim Parsen ab einer vorgegebenen Position. |
Parser-Beispiel mit Calendar-Objekt
Das folgende Beispiel zeigt ein ablauffähiges Demo, in dem zusätzlich die Zeit einem Calendar-Objekt zugewiesen wird. Mit einem zweiten formater holen wir dann wieder mit getTime() ein auf 1970 oder später beschränktes Datum-Objekt heraus und formatieren dies:
Listing 10.14
DateParser.java
import java.util.*;
import java.text.*;
public class DateParser
{
public static void main( String args[] )
{
DateFormat formater = DateFormat.getDateTimeInstance( );
try
{
Date date = formater.parse( "23.7.2002 12:54:56" );
Calendar cal =
new GregorianCalendar(TimeZone.getTimeZone("ECT"));
cal.setTime( date );
DateFormat formater2 = DateFormat.getDateTimeInstance(
DateFormat.LONG, DateFormat.LONG );
System.out.println( formater2.format( cal.getTime() ) );
} catch ( ParseException e ) { System.err.println( e ); }
}
}
Die Funktion parse() ist sehr empfindlich, wenn einige Felder nicht angegeben werden. Nach der Dokumentation sollte zwar ein Fehlen der Stunden nichts ausmachen, aber leider ist dann doch immer eine ParseException sicher, ja auch dann, wenn nur die Sekunden fehlen. Trotz dieser Unzulänglichkeiten ist die Klasse SimpleDateFormat sehr komplex. parse() ist (mit Kommentaren) 258 Zeilen lang. Davon werden noch einmal 233 Zeilen für subParse() und weitere 16 Zeilen für matchString(), eine Unterroutine von subParse(), aufgewendet.
10.8.3 Parsen und Formatieren ab bestimmten Positionen
 
Von den Methoden format() und parse() gibt es zwei Varianten, mit denen Teile eines Strings ausgegeben oder formatiert werden können. Zur Kapselung der Position dient ein neues Objekt ParsePosition. Dieses ist eine Klasse, die von format genutzt wird, um beim Parse-Prozess die aktuelle Position zu verwalten.
Die Klasse kapselt die Position so musterhaft, dass der Quellcode der Klasse hier einmal in Auszügen dargestellt wird:
public class ParsePosition {
int index = 0;
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
public ParsePosition(int index) {
this.index = index;
}
}
Obwohl wir eine private Variable index erwarten, kann auf diese durch die Klassen im eigenen Paket dennoch direkt zugegriffen werden. Ein Hoch auf die Datenkapselung und auf objektorientierte Programmierung.
1
Im November 1999 wurde ein Algorithmus zur Lösung des Jahr 2000-Problems patentiert, der einfach aussagt, dass zweistellige Jahreszahlen unter 30 zu 20XX und alle Jahreszahlen >30 zu 19XX zu ergänzen sind. Das Patent konnte allerdings erfolgreich angefochten werden.
|