4.8 Zeitdauern und der XML-Datentyp Duration *
Eine der Schwachstellen in der Datumsverarbeitung ist das Fehlen eines Typs für Dauern (wenn wir von TimeUnit einmal absehen). Ein eigenständiger Typ bringt Vorteile, wenn es zum Beispiel darum geht, Dauern zu addieren (»Was ergibt eine Woche plus 2 Monate?«) oder zu vergleichen (»Ist 1 Stunde mehr als 123456789 Millisekunden?«). Zwar lassen sich mit der add()-Methode von Calendar einzelne Segmente ändern, und dadurch lässt sich ein früherer oder späterer Zeitpunkt ansteuern, aber das ist wenig objektorientiert. Besser ist ein eigener Datentyp, der auch Operationen anbietet, um die Dauer auf ein Calendar- oder Date-Objekt zu setzen.
4.8.1 DatatypeFactory als Fabrik
Java 5 führte im Rahmen der W3C-XML-Schema-Unterstützung diverse neue Klassen ein, unter anderem den Typ Duration für Dauern nach der gregorianischen Zeit. Die Klasse liegt jedoch nicht im java.util-Paket, sondern wegen ihres XML-Bezugs im Paket javax.xml.datatype (wo es neben XMLGregorianCalendar nahezu alleine liegt). Exemplare von Duration werden auch von keinem Konstruktor angelegt, sondern von einer Fabrikklasse DatatypeFactory.
Beispiel |
Lege ein Duration-Objekt mit der Dauer von einem Tag und zwei Stunden an: Duration d = DatatypeFactory.newInstance().newDuration(true, 0, 0, 1, 2, 0, 0 ); |
Die DatatypeFactory bietet diverse Fabrikmethoden zum Anlegen der Duration-Objekte. Die Parameterlisten sind wie im Beispiel mitunter recht lang – in der längsten Variante gibt es einen Indikator für ein Vorzeichen, Jahr, Monat, Tag, Stunden, Minuten, Sekunden (keine Millisekunden oder genauer), also mit 7 Parametern. Ein Builder-Pattern zum Aufbau der Objekte wäre nett gewesen ...
abstract class javax.xml.datatype.DatatypeFactory |
- static DatatypeFactory newInstance() throws DatatypeConfigurationException
- Duration newDuration(boolean isPositive, int years, int months, int days, int hours,
int minutes, int seconds) - abstract Duration newDuration(boolean isPositive, BigInteger years, BigInteger months,
BigInteger days, BigInteger hours, BigInteger minutes, BigDecimal seconds) - abstract Duration newDuration(long durationInMilliSeconds)
- abstract Duration newDuration(String lexicalRepresentation)
- Duration newDurationDayTime(boolean isPositive, BigInteger day, BigInteger hour, BigInteger minute, BigInteger second)
- Duration newDurationDayTime(boolean isPositive, int day, int hour, int minute, int second)
- Duration newDurationDayTime(long durationInMilliseconds)
- Duration newDurationDayTime(String lexicalRepresentation)
- Duration newDurationYearMonth(boolean isPositive, BigInteger year, BigInteger month)
- Duration newDurationYearMonth(boolean isPositive, int year, int month)
- Duration newDurationYearMonth(long durationInMilliseconds)
- Duration newDurationYearMonth(String lexicalRepresentation)
4.8.2 Die Duration-Klasse und ihre Methoden
Die Duration-Ausgabe im Beispiel über toString() liefert eine besondere String-Repräsentation, die für XML-Dokumente interessant ist, aber andere Methoden sind interessanter. Eine grobe Einteilung ergibt:
- Anfragemethoden für die Segmente wie getYear(), getDay(), ...
- Vergleichsmethoden wie compare() oder isLongerThan()
- Duration-Objekte sind immutable, doch gibt es Methoden wie add() oder multiply(), die neue Duration-Objekte mit veränderten Segmenten zurückgeben, oder normalizeWith(), die Calendar-Felder zur Initialisierung nutzt.
- Anwenden der Duration-Objekte auf Calendar oder Date mit addTo()
Beispiel |
Addiere die Dauer von 2 Monaten und 3 Tagen, und berechne, wo wir dann relativ zu heute stehen. DatatypeFactory datatypeFactory = DatatypeFactory.newInstance(); |
Mögliche und unmögliche Operationen
Intern speichert die Duration-Implementierung jedes einzelne Segment und legt es nicht zu einer Zahl, etwa Sekunden zusammen. Das wäre auch nicht möglich, da die Anzahl Tage im Monat und im Jahr nicht immer gleich sind (dass zeigen uns der Februar und Schaltjahre). Wenn wir auf dem 1.1. einen Monat addieren, wollen wir beim 1.2. auskommen, und wenn wir bei 1.2. beginnen und einen Monat addieren, soll der 1.3. das Ergebnis sein.
Beispiel |
Additionen eines Monats auf einen Kalender: DatatypeFactory datatypeFactory = DatatypeFactory.newInstance(); |
Da die Anzahl der Tage im Monat und im Jahr beweglich ist, sind bei add() und substract() nur gewisse Kombinationen möglich. Es gibt Operationen, die Duration nicht ausführen kann und mit einer IllegalStateException bestraft. Während innerhalb der Gruppe Sekunden, Minuten, Stunden und Tage beliebig addiert und subtrahiert werden kann, ist der Übergang nach Monat und Jahr problematisch, insbesondere bei Subtraktionen.
Beispiel |
Ein Monat minus ein Tag ist genauso wenig möglich wie ein Jahr minus ein Tag. DatatypeFactory factory = DatatypeFactory.newInstance(); |
Duration-Vergleiche
Beim Vergleichen zweier Dauern gibt es vier unterschiedliche Ergebnisse, und daher implementiert Duration auch nicht die bekannte Comparable-Schnittstelle. Der Grund ist, dass einige Vergleiche nicht endscheidbar sind. So sind 30 Tage sind nicht automatisch 1 Monat, 365 Tage nicht automatisch 1 Jahr. Die compare()-Methode liefert Ganzahlen, die den jeweiligen Ausgang dokumentieren:
Vergleichsergebnis | Beispiel | Konstante |
Dauer1 ist kürzer als Dauer2 |
1 Minute ist kürzer als 100 Sekunden |
DatatypeConstants.LESSER |
Dauer1 ist länger als Dauer2 |
1 Tag ist länger als 1 Minute |
DatatypeConstants.GREATER |
Dauer1 ist gleichlang Dauer2 |
1 Minute ist gleich 60 Sekunden |
DatatypeConstants.EQUAL |
Dauer1 ist unvergleichbar mit Dauer2 |
30 Tage sind nicht automatisch 1 Monat |
DatatypeConstants.INDETERMINATE |
Beispiel |
Vergleiche 30 Tage mit einem Monat: DatatypeFactory factory = DatatypeFactory.newInstance(); |
Wir sprechen bei Duration daher auch nur von einer partiellen Ordnung statt von einer vollständigen Ordnung.
Zusammenfassung der Duration-Methoden
abstract class javax.xml.datatype.DatatypeFactory |
- abstract int getSign()
- int getYears()
- int getMonths()
- int getDays()
- int getHours()
- int getMinutes()
- int getSeconds()
- abstract Duration add(Duration rhs)
- Duration subtract(Duration rhs)
- Duration multiply(int factor)
- abstract Duration multiply(BigDecimal factor)
- abstract Duration negate()
- abstract int compare(Duration duration)
- boolean isLongerThan(Duration duration)
- boolean isShorterThan(Duration duration)
- abstract void addTo(Calendar calendar)
- void addTo(Date date)
- abstract Duration normalizeWith(Calendar startTimeInstant)
- long getTimeInMillis(Calendar startInstant)
- long getTimeInMillis(Date startInstant)
- abstract boolean isSet(DatatypeConstants.Field field)
- abstract Number getField(DatatypeConstants.Field field)
- QName getXMLSchemaType()
- abstract int hashCode()
- boolean equals(Object duration)
- String toString()
Ihr Kommentar
Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen.