{"id":2817,"date":"2014-05-16T14:36:42","date_gmt":"2014-05-16T12:36:42","guid":{"rendered":"http:\/\/www.tutego.de\/blog\/javainsel\/?p=2817"},"modified":"2014-05-16T14:37:55","modified_gmt":"2014-05-16T12:37:55","slug":"zeitdauern-xml-datentyp-duration","status":"publish","type":"post","link":"https:\/\/www.tutego.de\/blog\/javainsel\/2014\/05\/zeitdauern-xml-datentyp-duration\/","title":{"rendered":"Inselraus: Zeitdauern und der XML-Datentyp Duration"},"content":{"rendered":"<p>Eine der Schwachstellen in der Datumsverarbeitung ist das Fehlen eines Typs f\u00fcr Dauern (wenn wir von TimeUnit einmal absehen). Ein eigenst\u00e4ndiger Typ bringt Vorteile, wenn es zum Beispiel darum geht, Dauern zu addieren (\u00bbWas ergibt 1 Woche plus 2 Monate?\u00ab) oder zu vergleichen (\u00bbIst 1 Stunde mehr als 123456789 Millisekunden?\u00ab). Zwar lassen sich mit der add(\u2026)-Methode von Calendar einzelne Segmente \u00e4ndern, und dadurch l\u00e4sst sich ein fr\u00fcherer oder sp\u00e4terer 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.<\/p>\n<h3>DatatypeFactory als Fabrik<\/h3>\n<p>Im Rahmen der W3C-XML-Schema-Unterst\u00fctzung gibt es Klassen, unter anderem den Typ Duration f\u00fcr 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:\u00a0Lege ein Duration-Objekt mit der Dauer von 1 Tag und 2 Stunden an:<\/p>\n<pre>Duration d = DatatypeFactory.newInstance().newDuration(true, 0, 0, 1, 2, 0, 0 );\r\n System.out.println( d );\u00a0 \/\/ P0Y0M1DT1H0M0S<\/pre>\n<p>Die DatatypeFactory bietet diverse Fabrikmethoden zum Anlegen der Duration-Objekte. Die Parameterlisten sind wie im Beispiel mitunter recht lang \u2013 in der l\u00e4ngsten Variante gibt es einen Indikator f\u00fcr ein Vorzeichen, Jahr, Monat, Tag, Stunden, Minuten, Sekunden (keine Millisekunden oder genauer), also mit sieben Parametern. Ein Builder-Pattern zum Aufbau der Objekte w\u00e4re nett gewesen \u2026 abstract\u00a0class\u00a0javax.xml.datatype.DatatypeFactory<\/p>\n<ul>\n<li>staticDatatypeFactorynewInstance()throwsDatatypeConfigurationException<\/li>\n<li>DurationnewDuration(booleanisPositive,intyears,intmonths,intdays,inthours, intminutes,intseconds)<\/li>\n<li>abstractDurationnewDuration(booleanisPositive,BigIntegeryears,BigIntegermonths, BigIntegerdays,BigIntegerhours,BigIntegerminutes,BigDecimalseconds)<\/li>\n<li>abstractDurationnewDuration(longdurationInMilliSeconds)<\/li>\n<li>abstractDurationnewDuration(StringlexicalRepresentation)<\/li>\n<li>DurationnewDurationDayTime(booleanisPositive,BigIntegerday,BigIntegerhour,BigInteger \u00a0\u00a0minute,BigIntegersecond)<\/li>\n<li>DurationnewDurationDayTime(booleanisPositive,intday,inthour,intminute,intsecond)<\/li>\n<li>DurationnewDurationDayTime(longdurationInMilliseconds)<\/li>\n<li>DurationnewDurationDayTime(StringlexicalRepresentation)<\/li>\n<li>DurationnewDurationYearMonth(booleanisPositive,BigIntegeryear,BigIntegermonth)<\/li>\n<li>DurationnewDurationYearMonth(booleanisPositive,intyear,intmonth)<\/li>\n<li>DurationnewDurationYearMonth(longdurationInMilliseconds)<\/li>\n<li>DurationnewDurationYearMonth(StringlexicalRepresentation)<\/li>\n<\/ul>\n<h3>Die Duration-Klasse und ihre Methoden<\/h3>\n<p>Die Duration-Ausgabe im Beispiel \u00fcber toString() liefert eine besondere String-Repr\u00e4sentation, die f\u00fcr XML-Dokumente interessant ist, aber andere Methoden sind interessanter. Eine grobe Einteilung ergibt:<\/p>\n<ul>\n<li>Anfragemethoden f\u00fcr die Segmente wie getYear(), getDay(), \u2026<\/li>\n<li>Vergleichsmethoden wie compare(Duration) oder isLongerThan(Duration)<\/li>\n<li>Duration-Objekte sind immutable, doch gibt es Methoden wie add(Duration) oder multiply(Duration), die neue Duration-Objekte mit ver\u00e4nderten Segmenten zur\u00fcckgeben, oder die Methode normalizeWith(Calendar), die Calendar-Felder zur Initialisierung nutzt.<\/li>\n<li>Anwenden der Duration-Objekte auf Calendar oder Date mit addTo(Calendar)\/ addTo(Date).<\/li>\n<\/ul>\n<p>Beispiel:\u00a0Addiere die Dauer von 2 Monaten und 3 Tagen, und berechne, wo wir dann relativ zu heute stehen:<\/p>\n<pre>DatatypeFactory datatypeFactory = DatatypeFactory.newInstance();\r\n Duration d1 = datatypeFactory.newDurationYearMonth( true, 0, 2 );\r\n Duration d2 = datatypeFactory.newDuration( true, 0, 0, 3, 0, 0, 0 );\r\n Duration sum = d1.add( d2 );\r\n Date date = new Date();\r\n System.out.printf( \"%tF%n\", date ); \/\/ 2011-06-21\r\n sum.addTo( date );\r\n System.out.printf( \"%tF%n\", date ); \/\/ 2011-08-24<\/pre>\n<h4>M\u00f6gliche und unm\u00f6gliche Operationen<\/h4>\n<p>Intern speichert die Duration-Implementierung jedes einzelne Segment und legt es nicht zu einer Zahl, etwa Sekunden zusammen. Das w\u00e4re auch nicht m\u00f6glich, 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:\u00a0Additionen eines Monats auf einen Kalender:<\/p>\n<pre>DatatypeFactory datatypeFactory = DatatypeFactory.newInstance();\r\n Duration month = datatypeFactory.newDurationYearMonth( true, 0, 1 );\r\n Calendar cal = new GregorianCalendar( 2012, Calendar.JANUARY, 1 );\r\n month.addTo( cal );\r\n System.out.printf( \"%tF%n\", cal); \/\/ 2012-02-01\r\n month.addTo( cal );\r\n System.out.printf( \"%tF%n\", cal); \/\/ 2012-03-01<\/pre>\n<p>Da die Anzahl der Tage im Monat und im Jahr beweglich ist, sind bei add(\u2026) und substract(\u2026) nur gewisse Kombinationen m\u00f6glich. Es gibt Operationen, die Duration nicht ausf\u00fchren kann und mit einer IllegalStateException bestraft. W\u00e4hrend innerhalb der Gruppe Sekunden, Minuten, Stunden und Tage beliebig addiert und subtrahiert werden k\u00f6nnen, ist der \u00dcbergang nach Monat und Jahr problematisch, insbesondere bei Subtraktionen. Beispiel:\u00a01 Monat minus 1 Tag ist genauso wenig m\u00f6glich wie 1 Jahr minus 1 Tag:<\/p>\n<pre>DatatypeFactory factory = DatatypeFactory.newInstance();\r\n Duration year\u00a0 = factory.newDuration( true, 1, 0, 0, 0, 0, 0 );\r\n Duration month = factory.newDuration( true, 0, 1, 0, 0, 0, 0 );\r\n Duration day\u00a0\u00a0 = factory.newDuration( true, 0, 0, 1, 0, 0, 0 );\r\n year.subtract( day );\u00a0 \/\/ N\u00a0 IllegalStateException\r\n month.subtract( day ); \/\/ N\u00a0 IllegalStateException<\/pre>\n<h4>Duration-Vergleiche<\/h4>\n<p>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(\u2026)-Methode liefert Ganzahlen, die den jeweiligen Ausgang dokumentieren:<\/p>\n<table>\n<thead>\n<tr>\n<td width=\"190\"><strong>Vergleichsergebnis<\/strong><\/td>\n<td width=\"163\"><strong>Beispiel<\/strong><\/td>\n<td width=\"309\"><strong>Konstante<\/strong><\/td>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td width=\"190\">Dauer1 ist k\u00fcrzer als Dauer2.<\/td>\n<td width=\"163\">1 Minute ist k\u00fcrzer als 100 Sekunden<\/td>\n<td width=\"309\">DatatypeConstants.LESSER<\/td>\n<\/tr>\n<tr>\n<td width=\"190\">Dauer1 ist l\u00e4nger als Dauer2.<\/td>\n<td width=\"163\">1 Tag ist l\u00e4nger als 1 Minute<\/td>\n<td width=\"309\">DatatypeConstants.GREATER<\/td>\n<\/tr>\n<tr>\n<td width=\"190\">Dauer1 ist gleichlang Dauer2.<\/td>\n<td width=\"163\">1 Minute ist gleich 60 Sekunden<\/td>\n<td width=\"309\">DatatypeConstants.EQUAL<\/td>\n<\/tr>\n<tr>\n<td width=\"190\">Dauer1 ist unvergleichbar mit Dauer2.<\/td>\n<td width=\"163\">30 Tage sind nicht automatisch 1 Monat<\/td>\n<td width=\"309\">DatatypeConstants.INDETERMINATE<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><em>Ausgang von Vergleichen zwischen Duration-Objekten<\/em> Beispiel:\u00a0Vergleiche 30 Tage mit 1 Monat:<\/p>\n<pre>DatatypeFactory factory = DatatypeFactory.newInstance();\r\n Duration month\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 = factory.newDurationYearMonth( true, 0, 1 );\r\n Duration thirtyDays\u00a0 = factory.newDuration( true, 0, 0, 30, 0, 0, 0 );\r\n System.out.println( month.compare( thirtyDays ) ==\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 DatatypeConstants.INDETERMINATE );\u00a0\u00a0 \/\/ true\r\n System.out.println( month.isLongerThan( thirtyDays ) );\u00a0 \/\/ false\r\n System.out.println( thirtyDays.isLongerThan( month ) );\u00a0 \/\/ false<\/pre>\n<p>Wir sprechen bei Duration daher auch nur von einer partiellen Ordnung statt von einer vollst\u00e4ndigen Ordnung.<\/p>\n<h4>Zusammenfassung der Duration-Methoden<\/h4>\n<p>abstract\u00a0class\u00a0javax.xml.datatype.DatatypeFactory<\/p>\n<ul>\n<li>abstractintgetSign()<\/li>\n<li>intgetYears()<\/li>\n<li>intgetMonths()<\/li>\n<li>intgetDays()<\/li>\n<li>intgetHours()<\/li>\n<li>intgetMinutes()<\/li>\n<li>intgetSeconds()<\/li>\n<li>abstractDurationadd(Durationrhs)<\/li>\n<li>Durationsubtract(Durationrhs)<\/li>\n<li>Durationmultiply(intfactor)<\/li>\n<li>abstractDurationmultiply(BigDecimalfactor)<\/li>\n<li>abstractDurationnegate()<\/li>\n<li>abstractintcompare(Durationduration)<\/li>\n<li>booleanisLongerThan(Durationduration)<\/li>\n<li>booleanisShorterThan(Durationduration)<\/li>\n<li>abstractvoidaddTo(Calendarcalendar)<\/li>\n<li>voidaddTo(Datedate)<\/li>\n<li>abstractDurationnormalizeWith(CalendarstartTimeInstant)<\/li>\n<li>longgetTimeInMillis(CalendarstartInstant)<\/li>\n<li>longgetTimeInMillis(DatestartInstant)<\/li>\n<li>abstractbooleanisSet(DatatypeConstants.Fieldfield)<\/li>\n<li>abstractNumbergetField(DatatypeConstants.Fieldfield)<\/li>\n<li>QNamegetXMLSchemaType()<\/li>\n<li>abstractinthashCode()<\/li>\n<li>booleanequals(Objectduration)<\/li>\n<li>StringtoString()<\/li>\n<\/ul>\n<p>Seit dem in Java 8 die Java Date &amp; Time API eingezogen ist, muss man nicht mehr zu diesen Typen greifen, es sei denn, mann arbeitet direkt mit der XML-API.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Eine der Schwachstellen in der Datumsverarbeitung ist das Fehlen eines Typs f\u00fcr Dauern (wenn wir von TimeUnit einmal absehen). Ein eigenst\u00e4ndiger Typ bringt Vorteile, wenn es zum Beispiel darum geht, Dauern zu addieren (\u00bbWas ergibt 1 Woche plus 2 Monate?\u00ab) oder zu vergleichen (\u00bbIst 1 Stunde mehr als 123456789 Millisekunden?\u00ab). Zwar lassen sich mit der [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":"","_links_to":"","_links_to_target":""},"categories":[11],"tags":[],"class_list":["post-2817","post","type-post","status-publish","format-standard","hentry","category-insel"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/2817","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/comments?post=2817"}],"version-history":[{"count":2,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/2817\/revisions"}],"predecessor-version":[{"id":2819,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/2817\/revisions\/2819"}],"wp:attachment":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/media?parent=2817"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/categories?post=2817"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/tags?post=2817"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}