---
title: 'Inselraus: Zeitdauern und der XML-Datentyp Duration'
url: 'https://www.tutego.de/blog/javainsel/2014/05/zeitdauern-xml-datentyp-duration'
markdown: 'https://www.tutego.de/blog/javainsel/2014/05/zeitdauern-xml-datentyp-duration.md'
date: '2014-05-16'
description: '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 1 Woche plus 2 Monate?«) oder zu vergleichen (»Ist 1 Stunde mehr al…'
taxonomy:
  category:
    - Insel
---

# Inselraus: Zeitdauern und der XML-Datentyp Duration

![Illustration einer tropischen Insel mit Palmen und einem hohen Berg](https://www.tutego.de/blog/javainsel/user/themes/tutego/images/insel-cover.jpg)# Java Blog für Programmierer

[Java Blog für Programmierer](https://www.tutego.de/blog/javainsel/)

## Inselraus: Zeitdauern und der XML-Datentyp Duration

16.05.2014 · [Christian Ullenboom](https://www.tutego.de/blog/javainsel/author/admin/)

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 1 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.

### DatatypeFactory als Fabrik

Im Rahmen der W3C-XML-Schema-Unterstützung gibt es Klassen, 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 1 Tag und 2 Stunden an:

```java
Duration d = DatatypeFactory.newInstance().newDuration(true, 0, 0, 1, 2, 0, 0 );
 System.out.println( d );  // P0Y0M1DT1H0M0S
```

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 sieben Parametern. Ein Builder-Pattern zum Aufbau der Objekte wäre nett gewesen … abstract class `javax.xml.datatype.DatatypeFactory`

- `staticDatatypeFactorynewInstance()``throwsDatatypeConfigurationException`
- `DurationnewDuration(booleanisPositive,intyears,intmonths,intdays,inthours, intminutes,intseconds)`
- `abstractDurationnewDuration(booleanisPositive,BigIntegeryears,BigIntegermonths, BigIntegerdays,BigIntegerhours,BigIntegerminutes,BigDecimalseconds)`
- `abstractDurationnewDuration(longdurationInMilliSeconds)`
- `abstractDurationnewDuration(StringlexicalRepresentation)`
- `DurationnewDurationDayTime(booleanisPositive,BigIntegerday,BigIntegerhour,BigInteger minute,BigIntegersecond)`
- `DurationnewDurationDayTime(booleanisPositive,intday,inthour,intminute,intsecond)`
- `DurationnewDurationDayTime(longdurationInMilliseconds)`
- `DurationnewDurationDayTime(StringlexicalRepresentation)`
- `DurationnewDurationYearMonth(booleanisPositive,BigIntegeryear,BigIntegermonth)`
- `DurationnewDurationYearMonth(booleanisPositive,intyear,intmonth)`
- `DurationnewDurationYearMonth(longdurationInMilliseconds)`
- `DurationnewDurationYearMonth(StringlexicalRepresentation)`

### 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(Duration) oder isLongerThan(Duration)`
- `Duration`-Objekte sind immutable, doch gibt es Methoden wie `add(Duration)` oder `multiply(Duration)`, die neue `Duration`-Objekte mit veränderten Segmenten zurückgeben, oder die Methode `normalizeWith(Calendar)`, die Calendar-Felder zur Initialisierung nutzt.
- Anwenden der `Duration`-Objekte auf Calendar oder Date mit `addTo(Calendar)`/ `addTo(Date)`.

Beispiel: Addiere die Dauer von 2 Monaten und 3 Tagen, und berechne, wo wir dann relativ zu heute stehen:

```java
DatatypeFactory datatypeFactory = DatatypeFactory.newInstance();
 Duration d1 = datatypeFactory.newDurationYearMonth( true, 0, 2 );
 Duration d2 = datatypeFactory.newDuration( true, 0, 0, 3, 0, 0, 0 );
 Duration sum = d1.add( d2 );
 Date date = new Date();
 System.out.printf( "%tF%n", date ); // 2011-06-21
 sum.addTo( date );
 System.out.printf( "%tF%n", date ); // 2011-08-24
```

#### 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:

```java
DatatypeFactory datatypeFactory = DatatypeFactory.newInstance();
 Duration month = datatypeFactory.newDurationYearMonth( true, 0, 1 );
 Calendar cal = new GregorianCalendar( 2012, Calendar.JANUARY, 1 );
 month.addTo( cal );
 System.out.printf( "%tF%n", cal); // 2012-02-01
 month.addTo( cal );
 System.out.printf( "%tF%n", cal); // 2012-03-01
```

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 können, ist der Übergang nach Monat und Jahr problematisch, insbesondere bei Subtraktionen. Beispiel: 1 Monat minus 1 Tag ist genauso wenig möglich wie 1 Jahr minus 1 Tag:

```java
DatatypeFactory factory = DatatypeFactory.newInstance();
 Duration year  = factory.newDuration( true, 1, 0, 0, 0, 0, 0 );
 Duration month = factory.newDuration( true, 0, 1, 0, 0, 0, 0 );
 Duration day   = factory.newDuration( true, 0, 0, 1, 0, 0, 0 );
 year.subtract( day );  // N  IllegalStateException
 month.subtract( day ); // N  IllegalStateException
```

#### `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 SekundenDatatypeConstants.LESSERDauer1 ist länger als Dauer2.1 Tag ist länger als 1 MinuteDatatypeConstants.GREATERDauer1 ist gleichlang Dauer2.1 Minute ist gleich 60 SekundenDatatypeConstants.EQUALDauer1 ist unvergleichbar mit Dauer2.30 Tage sind nicht automatisch 1 MonatDatatypeConstants.INDETERMINATE

*Ausgang von Vergleichen zwischen `Duration`-Objekten* Beispiel: Vergleiche 30 Tage mit 1 Monat:

```java
DatatypeFactory factory = DatatypeFactory.newInstance();
 Duration month       = factory.newDurationYearMonth( true, 0, 1 );
 Duration thirtyDays  = factory.newDuration( true, 0, 0, 30, 0, 0, 0 );
 System.out.println( month.compare( thirtyDays ) ==
                     DatatypeConstants.INDETERMINATE );   // true
 System.out.println( month.isLongerThan( thirtyDays ) );  // false
 System.out.println( thirtyDays.isLongerThan( month ) );  // false
```

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`

- `abstractintgetSign()`
- `intgetYears()`
- `intgetMonths()`
- `intgetDays()`
- `intgetHours()`
- `intgetMinutes()`
- `intgetSeconds()`
- `abstractDurationadd(Durationrhs)`
- `Durationsubtract(Durationrhs)`
- `Durationmultiply(intfactor)`
- `abstractDurationmultiply(BigDecimalfactor)`
- `abstractDurationnegate()`
- `abstractintcompare(Durationduration)`
- `booleanisLongerThan(Durationduration)`
- `booleanisShorterThan(Durationduration)`
- `abstractvoidaddTo(Calendarcalendar)`
- `voidaddTo(Datedate)`
- `abstractDurationnormalizeWith(CalendarstartTimeInstant)`
- `longgetTimeInMillis(CalendarstartInstant)`
- `longgetTimeInMillis(DatestartInstant)`
- `abstractbooleanisSet(DatatypeConstants.Fieldfield)`
- `abstractNumbergetField(DatatypeConstants.Fieldfield)`
- `QNamegetXMLSchemaType()`
- `abstractinthashCode()`
- `booleanequals(Objectduration)`
- `StringtoString()`

Seit dem in Java 8 die Java Date & Time API eingezogen ist, muss man nicht mehr zu diesen Typen greifen, es sei denn, mann arbeitet direkt mit der XML-API.

Kategorien: [Insel](https://www.tutego.de/blog/javainsel/category/insel/)

[← Alle Beiträge](https://www.tutego.de/blog/javainsel/)

---

## Navigation

- Previous: [Vertragen sich Oracle und Apache?](https://www.tutego.de/blog/javainsel/2014/05/vertragen-sich-oracle-und-apache.md)
- Next: [Inselraus: Groovy](https://www.tutego.de/blog/javainsel/2014/05/inselraus-groovy.md)
