10.8 Formatieren der Datumsangaben
Nachdem wir eine Unterklasse von Calendar, nämlich GregorianCalendar, dazu benutzt haben, Datumswerte zu verwalten, wollen wir nun untersuchen, wie eine Formatierungsklasse dazu landestypische Ausgaben erzeugt. Unsere eigenen Ausgaben haben wir ja schon gemacht, doch es geht noch einfacher; die Java-Bibliothek kümmert sich selbstständig um die richtigen Ausgaben.
10.8.1 Mit DateFormat und SimpleDateFormat formatieren
Eine Klasse, die die Ausgabe und das Einlesen der Datum-Felder übernimmt, ist 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 zur Zerlegung von Datum-Zeichenketten sowie Methoden zur Ausgabe unter verschiedenen Sprachen und Formatierungen an.
Hier klicken, um das Bild zu Vergrößern
Beispiel Ausgabe des Datums ohne zusätzliche Formatierungsanweisungen:
Listing 10.10 DateFormater.java
import java.util.*;
import java.text.*;
public class DateFormater
{
public static void main( String args[] )
{
Calendar cal = new GregorianCalendar( TimeZone.getTimeZone("ECT") );
|
SimpleDateFormat formater = new SimpleDateFormat();
System.out.println( formater.format( cal.getTime() ) ); // 02.03.03 23:41
}
}
|
Um das Datum zu formatieren, müssen wir zunächst ein Exemplar von SimpleDateFormat 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. Die Ausgabe ist nicht ganz Jahr-20001-fest5.
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. |
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 möglichen Parametern DateFormat.SHORT, DateFormat.MEDIUM, DateFormat.LONG oder DateFormat.FULL an, die die Zeit beziehungsweise das Datum auf vier Arten formatieren:
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
|
Tabelle 10.3 Konstanten aus DateFormat und ihre Wirkung
Verschiedene statische Funktionen von DateFormat erzeugen direkt ein Formatier-Objekt:
abstract class java.text.DateFormat
extends Format
implements Cloneable
|
|
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. |
Hier klicken, um das Bild zu Vergrößern
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. Wir haben gesehen, dass unter Locale für einige Sprachen Konstanten vordefiniert sind. 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.11 SimpleDateFormater.java
import java.util.*;
import java.text.*;
public class SimpleDateFormater
{
public static void main( String args[] )
{
Calendar cal = new GregorianCalendar( TimeZone.getTimeZone("ECT") );
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:
Sonntag, 2. März 2003 23:45:08
domenica 2 marzo 2003 23.45.08
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. Nachfolgende Tabelle zeigt die erlaubten Symbole mit ihren Sonderbedeutungen. Mehrfach wiederholte Zeichen werden, wenn möglich, durch die Langform der jeweiligen Angabe ersetzt. Diese Symbole sind sprachunabhängig.
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
|
'
|
Tabelle 10.4 Symbole im Formatierungs-String zur Steuerung der Ausgabe
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 locale. |
|
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.12 ComplexDateFormater.java
import java.util.*;
import java.text.*;
public class ComplexDateFormater
{
public static void main( String args[] )
{
SimpleDateFormat fmt = new SimpleDateFormat();
fmt.applyPattern( "EEEE', 'dd. MMMM yyyy 'um' hh:mm:ss" );
Calendar cal = new GregorianCalendar();
System.out.println(fmt.format(cal.getTime())); // Montag, 02. Mai 2003 um 11:46:31
}
}
Einige Formatierungs-Strings für den deutschen Raum:
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
|
Tabelle 10.5 Symbole im Formatierungs-String zur Steuerung der Ausgabe
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 Standarddatum 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" );
Er sollte in der Praxis nicht benutzt werden.
Verschreiben wir uns aus Versehen im Formatierungs-String und ein Zeichen taucht nicht unter den erlaubten Zeichen 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'
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 Datum, welches das Format »Jahr-Monat-Tag« hat, mithilfe 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 taucht aber wieder das Problem auf, 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.
formater2 = 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 pos ) throws ParseException
Zerlegt einen Datum- oder einen Zeit-String und beginnt beim Parsen ab einer vorgegebenen Position. |
Parser-Beispiel mit Calendar-Objekt
Im nachfolgenden Beispiel wird die Zeit einem Calendar-Objekt zugewiesen. 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.13 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 und nicht leicht zu durchschauen.
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.
Hier klicken, um das Bild zu Vergrößern
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 dennoch durch die Klassen im eigenen Paket direkt zugegriffen werden. Ein Hoch auf die Datenkapselung und auf die 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.
|