<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Zeit für Java</title>
	<atom:link href="http://www.menodata.de/blog/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.menodata.de/blog</link>
	<description>Meno Hochschilds Java-Blog</description>
	<lastBuildDate>Mon, 25 May 2015 13:30:43 +0000</lastBuildDate>
	<language>de-DE</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.4.1</generator>
		<item>
		<title>Fortschritte in Time4J</title>
		<link>http://www.menodata.de/blog/2015/05/25/fortschritte-in-time4j/</link>
		<comments>http://www.menodata.de/blog/2015/05/25/fortschritte-in-time4j/#comments</comments>
		<pubDate>Mon, 25 May 2015 13:30:43 +0000</pubDate>
		<dc:creator>Meno Hochschild</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Time4J]]></category>

		<guid isPermaLink="false">http://www.menodata.de/blog/?p=245</guid>
		<description><![CDATA[Eine lange Zeit ist seit meinem letzten Blog-Artikel verstrichen, weil ich all meine freie Zeit in das Time4J-Projekt gesteckt habe. Viele Versionen sind veröffentlicht worden. Heute habe ich die Versionen 3.1 und 4.0 veröffentlicht. Die Versionslinien 1.x und 2.x sind &#8230; <a href="http://www.menodata.de/blog/2015/05/25/fortschritte-in-time4j/">Weiterlesen <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Eine lange Zeit ist seit meinem letzten Blog-Artikel verstrichen, weil ich all meine freie Zeit in das <a href="https://github.com/MenoData/Time4J" target="_blank">Time4J-Projekt</a> gesteckt habe. Viele Versionen sind veröffentlicht worden. Heute habe ich die <a href="http://search.maven.org/#search|ga|1|time4j" target="_blank">Versionen 3.1 und 4.0</a> veröffentlicht.</p>
<p>Die Versionslinien 1.x und 2.x sind mittlerweile veraltet und als Experimentierphase zu werten, weil sich das Design der Version 1.0 aus guten Gründen doch inkompatibel ändern musste.</p>
<ul>
<li>Die Versionsline 1.x hatte keine saubere Trennung zwischen lokalen und globalen Typen.</li>
<li>Die Versionslinie 2.x war in ihrem Kern für Android einfach zu groß.</li>
</ul>
<p>Aber die Versionslinien 3.x für Java 6+7 (und in Zukunft Android) sowie die neue Versionsline 4.x für Java 8 sind jetzt ausgereift und stabil. <strong>Inkompatible Änderungen habe ich in Zukunft nicht mehr vor. </strong>Das ist nun in Stein gemeißelt.</p>
<p>Die neue Version v4.0, so kann man mit gutem Recht feststellen, hat nun endgültig die so vielgerühmte Joda-Time-Bibliothek und auch die in Java-8 eingebaute Zeitbibliothek (JSR-310) überholt, was die Anzahl und Qualität der unterstützten Features angeht. Lediglich einige Kalendersysteme fehlen im Vergleich.</p>
<p>Was fehlt noch? Gibt es ein Problem? Tja, Time4J ist noch so gut wie unbekannt, auch weil ich so gut wie nicht die Werbetrommel gerührt habe, sondern nur auf die Entwicklung fokussiert war. Und ein Marketing-Profi bin ich nicht, muß ich offenherzig bekennen. Trotzdem bekenne ich mich weiterhin zur Pflege und Weiterentwicklung dieser jetzt schon formidablen Bibliothek. Es gibt noch viel zu tun. Die Bibliothek ist bei weitem nicht fertig. Ich habe noch sehr viele Ideen. Allerdings werde ich jetzt die (extreme) Entwicklungsgeschwindigkeit verlangsamen, weil meine Gesundheit und meine Familie nicht weiter vernachlässigt werden dürfen.</p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.menodata.de/blog/2015/05/25/fortschritte-in-time4j/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Unterstützung für Apache Maven in Time4J</title>
		<link>http://www.menodata.de/blog/2014/08/14/unterstutzung-fur-apache-maven-in-time4j/</link>
		<comments>http://www.menodata.de/blog/2014/08/14/unterstutzung-fur-apache-maven-in-time4j/#comments</comments>
		<pubDate>Thu, 14 Aug 2014 03:58:10 +0000</pubDate>
		<dc:creator>Meno Hochschild</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Time4J]]></category>

		<guid isPermaLink="false">http://www.menodata.de/blog/?p=239</guid>
		<description><![CDATA[Die neue Version v1.1 von Time4J bietet nun direkte Unterstützung für Apache Maven dank der integrierten pom-Dateien, auch ist die neue Version endlich im Maven Central Repository verfügbar. Der Funktionsumfang ist im Vergleich zu v1.0 praktisch gleich geblieben. Für alle &#8230; <a href="http://www.menodata.de/blog/2014/08/14/unterstutzung-fur-apache-maven-in-time4j/">Weiterlesen <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Die neue Version v1.1 von Time4J bietet nun direkte Unterstützung für Apache Maven dank der integrierten pom-Dateien, auch ist die neue Version endlich im Maven Central Repository verfügbar. Der Funktionsumfang ist im Vergleich zu v1.0 praktisch gleich geblieben.</p>
<p>Für alle Anwender, die NICHT Maven als build-Tool verwenden, gibt es den folgenden Link zum Download:</p>
<p><a href="https://github.com/MenoData/Time4J/releases/tag/v1.1">https://github.com/MenoData/Time4J/releases/tag/v1.1</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.menodata.de/blog/2014/08/14/unterstutzung-fur-apache-maven-in-time4j/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Time4J-Kern fertig</title>
		<link>http://www.menodata.de/blog/2014/07/04/time4j-kern-fertig/</link>
		<comments>http://www.menodata.de/blog/2014/07/04/time4j-kern-fertig/#comments</comments>
		<pubDate>Fri, 04 Jul 2014 04:10:05 +0000</pubDate>
		<dc:creator>Meno Hochschild</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Time4J]]></category>

		<guid isPermaLink="false">http://www.menodata.de/blog/?p=230</guid>
		<description><![CDATA[Gerade ist die neue Version v1.0 von Time4J in ihrem Kern fertig. Sie ist unter der Lizenz LGPLv2.1 erhältlich (die gleiche Lizenz wie sie Wildfly (früher JBoss) verwendet). Diese Version ist nunmehr produktionsreif erhältlich. Folgender Link kann für den Download &#8230; <a href="http://www.menodata.de/blog/2014/07/04/time4j-kern-fertig/">Weiterlesen <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Gerade ist die neue Version v1.0 von Time4J in ihrem Kern fertig. Sie ist unter der Lizenz <strong>LGPLv2.1</strong> erhältlich (die gleiche Lizenz wie sie Wildfly (früher JBoss) verwendet). Diese Version ist nunmehr produktionsreif erhältlich. Folgender Link kann für den Download verwendet werden:</p>
<p><a href="https://github.com/MenoData/Time4J/releases/tag/v1.0" target="_blank">Release v1.0</a></p>
<p>Und die nächsten Aufgaben warten schon: Intervalle und Formatierung von Zeitdauern (<a href="https://github.com/MenoData/Time4J/issues/milestones" target="_blank">milestone M3</a>).</p>
]]></content:encoded>
			<wfw:commentRss>http://www.menodata.de/blog/2014/07/04/time4j-kern-fertig/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Time4J endlich erschienen</title>
		<link>http://www.menodata.de/blog/2014/02/17/time4j-endlich-erschienen/</link>
		<comments>http://www.menodata.de/blog/2014/02/17/time4j-endlich-erschienen/#comments</comments>
		<pubDate>Mon, 17 Feb 2014 01:03:49 +0000</pubDate>
		<dc:creator>Meno Hochschild</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Time4J]]></category>

		<guid isPermaLink="false">http://www.menodata.de/blog/?p=216</guid>
		<description><![CDATA[Lange angekündigt, ist meine Java-Datums-und Zeitbibliothek Time4J in der Version v0.1-alpha veröffentlicht worden. Wo? Zu finden auf: http://github.com/MenoData/Time4J Zweieinhalb Jahre harter Arbeit liegen da hinter mir, und meine Familie hat schon einige Male ganz schön geseufzt. Aber ich denke, das &#8230; <a href="http://www.menodata.de/blog/2014/02/17/time4j-endlich-erschienen/">Weiterlesen <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Lange angekündigt, ist meine Java-Datums-und Zeitbibliothek Time4J in der Version v0.1-alpha veröffentlicht worden. Wo? Zu finden auf:</p>
<p><a href="http://github.com/MenoData/Time4J">http://github.com/MenoData/Time4J</a></p>
<p>Zweieinhalb Jahre harter Arbeit liegen da hinter mir, und meine Familie hat schon einige Male ganz schön geseufzt. Aber ich denke, das Ergebnis kann sich sehen lassen, auch wenn natürlich dieser Erstversion noch einige Features fehlen. Und nach der Arbeit ist bekanntlich vor der Arbeit. Die Entwicklung bleibt nicht stehen, sondern geht selbstverständlich weiter. Schwerpunkt des nächsten Release v0.2-alpha werden die Umstellung der Dokumentation auf Englisch, extra Tutorials und mehr Zeitzonen-Features sein.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.menodata.de/blog/2014/02/17/time4j-endlich-erschienen/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Über die &#8220;Java-Zeitskala&#8221;</title>
		<link>http://www.menodata.de/blog/2013/12/22/uber-die-java-zeitskala/</link>
		<comments>http://www.menodata.de/blog/2013/12/22/uber-die-java-zeitskala/#comments</comments>
		<pubDate>Sun, 22 Dec 2013 15:29:41 +0000</pubDate>
		<dc:creator>Meno Hochschild</dc:creator>
				<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://www.menodata.de/blog/?p=211</guid>
		<description><![CDATA[Wegen der &#8220;Java-Zeitskala&#8221; habe ich eine Debatte mit den Erfindern dieser Zeitskala losgetreten. Nicht, daß ich den POSIX-Standard gut fände, aber ich befürworte eine gewisse Konsequenz, die meiner Meinung nach im JSR-310 etwas fehlt. Außerdem bin ich der Meinung, daß &#8230; <a href="http://www.menodata.de/blog/2013/12/22/uber-die-java-zeitskala/">Weiterlesen <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Wegen der &#8220;Java-Zeitskala&#8221; habe ich <a href="https://github.com/ThreeTen/threeten/issues/343" target="_blank">eine Debatte</a> mit den Erfindern dieser Zeitskala losgetreten. Nicht, daß ich den POSIX-Standard gut fände, aber ich befürworte eine gewisse Konsequenz, die meiner Meinung nach im JSR-310 etwas fehlt. Außerdem bin ich der Meinung, daß eine Zeitbibliothek an POSIX nicht vorbeikommt und eine UTC-Unterstützung nur parallel und nicht ersatzweise anbieten sollte.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.menodata.de/blog/2013/12/22/uber-die-java-zeitskala/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Die neue Zeitbibliothek von Java8 &#8211; Eine Rezension</title>
		<link>http://www.menodata.de/blog/2013/12/02/die-neue-zeitbibliothek-von-java8-eine-rezension/</link>
		<comments>http://www.menodata.de/blog/2013/12/02/die-neue-zeitbibliothek-von-java8-eine-rezension/#comments</comments>
		<pubDate>Mon, 02 Dec 2013 11:59:37 +0000</pubDate>
		<dc:creator>Meno Hochschild</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Java 8]]></category>
		<category><![CDATA[JSR-310]]></category>
		<category><![CDATA[Threeten]]></category>

		<guid isPermaLink="false">http://www.menodata.de/blog/?p=174</guid>
		<description><![CDATA[Am 4. Dezember 2013 geht der sogenannte &#8220;Public Review&#8221; des JSR 310 zu Ende. Das ist eine gute Gelegenheit für eine umfassende Rezension. Das vorgeschlagene API hat sich endlich auch stabilisiert. Vorher war es so häufig großen Änderungen unterworfen, daß &#8230; <a href="http://www.menodata.de/blog/2013/12/02/die-neue-zeitbibliothek-von-java8-eine-rezension/">Weiterlesen <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Am 4. Dezember 2013 geht der sogenannte &#8220;Public Review&#8221; des JSR 310 zu Ende. Das ist eine gute Gelegenheit für eine umfassende Rezension. Das vorgeschlagene API hat sich endlich auch stabilisiert. Vorher war es so häufig großen Änderungen unterworfen, daß eine frühe Rezension schnell überholt gewesen wäre. Diese Rezension überläßt Code-Beispiele gerne dem <a href="http://docs.oracle.com/javase/tutorial/datetime/" target="_blank">Oracle-Tutorial</a> und fokussiert sich mehr auf das allgemeine Design und die Benutzbarkeit des APIs. Detaillierte Code-Vergleiche und Code-Inspektionen hätten auch den Rahmen dieser Rezension gesprengt. Kalendarisches Spezialwissen ist neben guten Kenntnissen des JSR-310 sicherlich auch nötig, um die Tiefen dieser Rezension zu erfassen. Also eher eine Rezension für anspruchsvolle Entwickler.</p>
<h1>Die großen Vorteile auf einen Blick</h1>
<ul>
<li>Immutable und thread-safe (unveränderlich &#8211; sicher in der Parallelverarbeitung)</li>
<li>Fluent Style (flüssiger Programmierstil)</li>
<li>Einführung neuer elementarer Datentypen wie: Reines Datum (LocalDate) oder reine Uhrzeit (LocalTime), das ist ein Riesengewinn im Vergleich zum alten JDK</li>
<li>Extra Formatfabrik zur programmatischen Erstellung individueller Zeitformate</li>
<li>Nano-Präzision zur Abbildung einiger Datenbankzeitstempel</li>
<li>Differenzberechnungen (mit Hilfe von java.time.Period und java.time.Duration)</li>
<li>API für julianische Tage (siehe Klasse java.time.temporal.JulianFields)</li>
<li>API für zeitzonenrelevante Abfragen (z.B. ZoneOffsetTransition#isGap())</li>
<li>Umalqra-Kalender für Saudi-Arabien (ein Verdienst von Oracle und Roger Riggs)</li>
<li>Allgemeiner Mechanismus für benutzerdefinierte Abfragen und Manipulationen via TemporalQuery und TemporalAdjuster</li>
</ul>
<p>Können wir uns jetzt alle zufrieden zurücklehnen und sagen: Ende gut, alles gut? Für die meisten Business-Entwickler mit eher geringen Ansprüchen und Erwartungen mit Abstrichen ja, für anspruchsvolle Entwickler leider nicht. Ich kann eine noch längere Liste von Nachteilen präsentieren. Schauen wir uns alles noch einmal im Detail an.<span id="more-174"></span></p>
<h2>Design-Mangel 1: Keine Typsicherheit zur Kompilierzeit</h2>
<p>Um Typsicherheit zur Kompilierzeit zu gewährleisten, muß ein API entweder nur konkrete Typen verwenden, oder eben das seit Java 5 (JSR 14) angebotene Sprach-Feature der Generics verwenden. Letzteres fehlt weitgehend. Das liegt hauptsächlich daran, daß der Projektleiter des JSR-310 Stephen Colebourne generell gegenüber Generics feindlich gegenübersteht. Zuletzt schrieb er in der <a href="https://java.net/projects/javamoney/lists/jcurrency_mail/archive/2013-11/message/23" target="_blank">JSR-354-Mailing-Liste</a>:</p>
<blockquote><p>In JSR-310 I found no way to get generics to actually<br />
work. The problem which 310 tackles involves many more interacting interfaces, and the increased friction between the types nullifies the benefits of generics.</p></blockquote>
<p>Ich werde noch mit meiner Bibliothek Time4J und mit Hilfe von self-referencing Generics den Beweis antreten, daß es doch geht. Was bedeutet die Nicht-Umsetzung der Typsicherheit konkret in der Praxis? Hier ein Beispiel:</p>
<pre>import static java.time.temporal.ChronoUnit.HOURS;
LocalDate date = LocalDate.of(2013, 11, 30).plus(30, HOURS);
// throws RuntimeException</pre>
<p>Das gesamte API ist stark auf Ausnahmen zur Laufzeit gebaut. Das betrifft in erster Linie die Verwendung des Subpakets java.time.temporal. Nicht so toll. Am besten verfahren Anwender als Umgehungslösung, wenn sie so weit wie möglich konkrete Typen und Methoden verwenden, im genannten Beispiel wäre die Alternative:</p>
<pre>LocalDate date = LocalDate.of(2013, 11, 30).plusDays(30);
// plusHours() gibt es im Gegensatz zu plus(30, HOURS) nicht.</pre>
<h1>Design-Mangel 2: Zuviele Methoden</h1>
<p>Das Fehlen der Typsicherheit führt gleich auch zum zweiten Mangel, nämlich zu einer kaum noch überschaubaren Anzahl von Methoden pro Klasse. So hat etwa die Klasse LocalDate 64 öffentliche Methoden, die Klassen LocalDateTime und OffsetDateTime sogar 80 und die Klasse ZonedDateTime 84 Methoden. Empfohlen wird in der Regel nur max. 30-50 Methoden pro Klasse. Das liegt vor allem daran, daß einer generischen und leicht besser lesbaren Methode wie plus(x, TemporalUnit) noch weitere Methoden wie plusDays(), plusHours() etc. zur Seite gestellt werden. Der JSR 310 versucht, die Unübersichtlichkeit u.a. mit der konsequenten Verwendung von Methodenpräfixen wie plus, minus, of, at, to, with, get usw. auszugleichen. Das ist für sich genommen zwar gut, aber echte Mehrfunktionalität ergibt sich dadurch nicht.</p>
<p>Interessanterweise gibt es dafür keine Konstruktoren. Hier gibt es Vor- und Nachteile. Während in <em>immutable</em> Klassen Fabrikmethoden z.B. wegen besserer Cache-Unterstützung oder wegen eines flüssigen Programmierstils allgemein vorzuziehen sind, sind Konstruktoren zumindest intuitiv leichter zu finden, wenigstens für Einsteiger. Ich meine insgesamt, hie und da hätte der eine oder andere Konstruktor nicht wirklich geschadet.</p>
<h1>Design-Mangel 3: Keine klare Zeitskalendefinition</h1>
<p>Die Spezifikation des JSR-310 spricht von zwei Zeitskalen: Human time line und Machine time line. Der Unterschied wird daran festgemacht, daß erstere mit Feldern wie Jahr, Monat, Tag, Stunde, Minute, Sekunde etc. arbeitet und die sog. maschinelle Zeit mit einer einzigen fortlaufenden Nummer (Anzahl der verstrichenen Sekunden seit der UNIX-Epoche 1970-01-01T00:00:00Z). Der JSR-310-Guide (Version 0.7) stellt hier insbesondere die Klassen java.time.Instant und java.time.Duration in den Fokus der maschinellen Zeit.</p>
<p>Hier wird schon mal übersehen, daß auch ein Instant durchaus eine Feldsicht bietet und sie sogar definiert:</p>
<ul>
<li><code>NANO_OF_SECOND</code></li>
<li><code>MICRO_OF_SECOND</code></li>
<li><code>MILLI_OF_SECOND</code></li>
<li><code>INSTANT_SECONDS</code></li>
</ul>
<p>Also ist demnach ein Instant sowohl eine fortlaufende Nummer (INSTANT_SECONDS) als auch eine Sammlung weiterer Felder wie NANO_OF_SECOND etc. Andererseits sind Klassen wie java.time.LocalDate nicht nur per Feldsicht definiert. Neben Jahr, Monat und Tag des Monats bietet ein LocalDate auch eine fortlaufende Nummer! =&gt; Die sogenannten Epochentage (im JSR-310-API immer relativ zur UNIX-Epoche). Es handelt sich tatsächlich um eine Variante der julianischen Tage.</p>
<p>Wir sehen, die künstliche Trennung zwischen &#8220;Human-scale time&#8221; und &#8220;Machine-scale time&#8221; ist gar keine. Andererseits lesen wir in der Javadoc der Klasse Instant Folgendes über eine &#8220;Java time-scale&#8221;:</p>
<blockquote><p>The Java time-scale is used for all date-time classes. This includes <code>Instant</code>, <code>LocalDate</code>, <code>LocalTime</code>, <code>OffsetDateTime</code>, <code>ZonedDateTime</code> and <code>Duration</code>.</p></blockquote>
<p>Abgesehen davon, daß hier wahrscheinlich die Klasse LocalDateTime vergessen wurde, fragt man sich schon, wie die &#8220;Java time-scale&#8221; alle möglichen grundverschiedenen Typen abdecken kann. Was also ist diese Zeitskala?</p>
<blockquote><p>There are currently, as of 2013, two segments in the Java time-scale.</p>
<p>&nbsp;</p>
<p>For the segment from 1972-11-03 (exact boundary discussed below) until further notice, the consensus international time scale is UTC (with leap seconds). In this segment, the Java Time-Scale is identical to <a href="http://www.cl.cam.ac.uk/%7Emgk25/time/utc-sls/">UTC-SLS</a>. This is identical to UTC on days that do not have a leap second. On days that do have a leap second, the leap second is spread equally over the last 1000 seconds of the day, maintaining the appearance of exactly 86400 seconds per day.</p>
<p>&nbsp;</p>
<p>For the segment prior to 1972-11-03, extending back arbitrarily far, the consensus international time scale is defined to be UT1, applied proleptically, which is equivalent to the (mean) solar time on the prime meridian (Greenwich). In this segment, the Java Time-Scale is identical to the consensus international time scale. The exact boundary between the two segments is the instant where UT1 = UTC between 1972-11-03T00:00 and 1972-11-04T12:00.</p></blockquote>
<p>Es ist völlig unklar, warum eine solche offensichtlich sekundenbasierte Zeitskala auch für einen Typ wie LocalDate gelten soll. Ich verstehe und definiere eine Zeitskala als eine Abbildung von einer beliebigen Menge von Zeitelementen (Feldsicht) auf eine fortlaufende Nummer. Demnach verwendet ein LocalDate eine Abbildung auf julianische Tage, ein Instant hingegen auf Sekunden und Nanosekunden seit der UNIX-Epoche. Das sind klar zwei verschiedene Zeitskalen.</p>
<p>Aber die Verwirrung geht noch weiter: Während die Trennung einer Zeitskala in Segmente durchaus sinnvoll sein kann (um eine tiefgreifende Änderung der zivilen Zeitskala zu modellieren, z.B. Wechsel von der astronomischen Zeit zur Atomuhrzeit mit der Einführung von UTC zum genauen Zeitpunkt 1972-01-01T00:00:00Z), stellt sich hier die Frage, warum ausgerechnet ein nicht präziser Zeitpunkt wie 1972-11-03/04 angegeben ist. Die Tatsache, daß dieser Zeitpunkt nur mit einer Unsicherheit von 36 Stunden gegeben ist, macht eine präzise Implementierung in einer java.time.Clock quasi zur Unmöglichkeit. Was steckt eigentlich dahinter?</p>
<p>Das JSR-310-Team will auf keinen Fall die UTC-Schaltsekunden dem Anwender präsentieren, sondern sie verstecken (angeblich weil die meisten Entwickler die UTC-Definition nach Ansicht des API-Teams nicht verstehen können). Deshalb ist eine einfache UTC-Implementierung nicht gewollt. Stattdessen hat S. Colebourne den Versuch gemacht, den obsoleten Vorschlag UTC-SLS von Markus Kuhn wiederzubeleben und ihn auf Standard-Java-Software auszuweiten, was vielleicht nicht im Sinn von Markus Kuhn war. Dieser Vorschlag besagt vereinfacht, in den letzten 1000 Sekunden vor einer Schaltsekunde extra Millisekunden so einzuführen, daß die Schaltsekunde selbst nicht mehr im Ergebnis auftaucht. Das ist eine ähnliche Idee, wie sie auch von Googles NTP-Servern als technische Umgehungslösung (über einen ganzen Tag statt über 1000 Sekunden verteilt) realisiert wurde, um Clients zu bedienen, die mit Angaben wie T23:59:60Z nichts anfangen können. Damit ein solcher geglätteter Algorithmus auch beim Segmentwechsel von UT1 zu UTC funktioniert und es keinen plötzlichen Sprung gibt, bezieht sich der JSR 310 auf den ersten Zeitpunkt nach Einführung von UTC, zu dem galt: UT1 = UTC, nämlich ca. 1972-11-03/04.</p>
<p>Zu raffiniert gedacht, um Schaltsekunden zu verstecken, denn aufgrund der vorhandenen IERS-Daten aus dem Jahre 1972 ist ein solcher Segment-Wechsel auf der &#8220;Java time-scale&#8221; de facto nicht präzise zu definieren (nur numerische Interpolation möglich). Aber offenbar will der JSR-310 auch keine Präzision, denn:</p>
<blockquote><p>Implementations of the Java time-scale using the JSR-310 API are not required to provide any clock that is sub-second accurate, or that progresses monotonically or smoothly. Implementations are therefore not required to actually perform the UTC-SLS slew or to otherwise be aware of leap seconds.</p></blockquote>
<p>Ein Offenbarungseid. Wenn die Präzision egal ist, was soll eigentlich das ganze Gerede über UTC-Schaltsekunden noch? Ein neues Standard-API sollte vor allem eine einheitliche Norm für Anwender definieren, tut es hier aber ausdrücklich nicht. Das ist auch deshalb schlimm, weil das API Nanosekundengenauigkeit definiert, die tatsächlich per Design/Spezifikation nicht geliefert wird, so daß oft unklar ist, wie insbesondere Subsekundenteile verschiedener Datentypen zu interpretieren und zu konvertieren sind. Lediglich die Dokumention der gewählten Uhrenimplementierung wird von externen Implementierungen verlangt, mehr nicht. Offenbar ist java.time.Instant keine UTC-SLS-Implementierung (soll es aber sein???). Mehr noch, im JDK 8 wird es gar keine reale Implementierung der Zeitskala UTC-SLS geben.</p>
<p>Interessanterweise ist auch bis jetzt (siehe <a href="https://github.com/ThreeTen/threeten/issues/343" target="_blank">Issue 343</a>) eine entsprechende Dokumentation der alten aber leider unvermeidlichen Klasse java.util.Date ausgeblieben. Mal sehen, ob wenigstens hier das JSR-310-Team die Dokumentation nachbessert.</p>
<p>Die ominöse &#8220;Java time-scale&#8221; wird also bis jetzt nur spezifiziert. Deren Implementierung wird nicht verlangt, gilt aber trotzdem für eine Reihen von Datentypen von LocalDate bis Instant (nur SOLL, aber nicht IST). Mehr Konfusion geht kaum. Die Spezifikation ist im Ergebnis beliebig. Ich prophezeie, daß Anwender real sich nicht um die JSR-310-Spezifikation kümmern werden und fröhlich weiter mit dem weit verbreiteten POSIX-Standard arbeiten, so wie sie es schon immer mit der Klasse java.util.Date gemacht haben. Eins ist auch klar, der JSR-310 wird so niemals eine sekundengenaue Brücke zur (kalendarischen) Astronomie schlagen können, weil Zeitskalen wie UTC, UT1, TT, TAI etc. gar nicht erst angeboten werden. Das schon deshalb, weil Oracle sich von der technischen Basis solcher Zeitskalen offiziell verabschiedet hat. Die Vorhaltung einer speziellen Schaltsekundentabelle ist aus dem JDK-Repository wieder entfernt worden (war mal vorhanden!).</p>
<p>Und wer wie Colebourne glaubt, daß sich das Zeitskalenproblem durch eine mögliche Eliminierung der UTC-Schaltsekunden auf einer zukünftigen internationalen Konferenz der ITU 2015 von selbst erledigt, ist gründlich schief gewickelt. Dann wird eben nicht der Schaltsekunden-Mechanismus zur Umrechnung von Zeitskalen herhalten, sondern eine deutlich komplexere Materie rund um den astronomischen Term Delta-T, was teilweise jetzt schon zur Umrechnung von UT1-Angaben notwendig ist.</p>
<p>Letztlich besteht der Kern der Differenz zwischen mir und Colebourne nicht bloß in der Frage der Unterstützung von UTC-Schaltsekunden, sondern vielmehr darin, ob eine Java- Zeitbibliothek eine eigene Zeitskala definieren soll, die außerhalb von Java keine Rolle spielt, oder ob stattdessen die in der externen Realität vorhandenen Zeitskalen modelliert werden sollen.</p>
<h1>Design-Mangel 4: Ungenügende Kalenderabstraktion</h1>
<p>Das java.time.chrono-Paket definiert Kalendersysteme auf der Basis von Ära, Jahr, Monat, und Tag. Das ist viel zu konkret. Viele Chronologien (inklusive der internationalen Norm ISO-8601!) haben gar keine Ära. Hier hat sich Oracle mit seinen Design-Vorstellungen gegen S. Colebourne durchgesetzt. De facto liegen die gleichen Grundgedanken wie der alten Klasse java.util.Calendar vor. Internationale Kalenderanwendungen sollen gegen einen abstrakten Datumstyp (hier java.time.chrono.ChronoLocalDate) programmiert werden. Colebourne hat zu Recht eingewandt, daß die zugehörigen Abstraktionen wie getYear(), getMonth() etc. dann völlig andere Werte als erwartet liefern können und daher eine reichhaltige Quelle für Programmierfehler darstellen. Zum Beispiel ist das Jahr im buddhistischen Kalender in Thailand derzeit jenseits der Zahl 2500. Oder es kann mal 13 Monate statt 12 Monate geben. Interessenten können sich die Dokumentation der Klasse ChronoLocalDate zu Gemüte führen. Es ist eine einzige große Warnung. Zwischen den Zeilen steht quasi die Aufforderung: Bitte benutze mich nicht. Das sagt eigentlich schon alles über den Gebrauchswert des ganzen Pakets.</p>
<p>Trotzdem hat jemand aus der Java-Community schon versucht, eine <a href="https://github.com/ThreeTen/threetenbp-extra/pull/2" target="_blank">Implementierung des diskordianischen Kalenders</a> auf dieser Basis aufzubauen. Es ist nicht besonders gut gelungen, denn als Ersatzkonstrukt für die in diesem Kalendersystem verwendenten 5 &#8220;seasons&#8221; werden allgemeine Monate benutzt. Mit Verwendung des Format-APIs des JSR-310 ergeben sich dadurch völlig falsche Bezeichnungen in der Formatierung. Kein Wunder, leider gibt es auch kein Tutorial oder Lernbeispiel, wie überhaupt neue Kalendersysteme mit dem JSR-310 zu entwickeln sind. Es werden auch längst nicht alle Kalendersysteme im JDK selbst enthalten sein. So fehlt der im Threeten-Projekt schon fertig implementierte koptische Kalender &#8211; und wird wohl nie kommen. Wer also viele Kalendersysteme erwartet, wird durch das JDK nicht bedient werden und muß so oder so eine externe (Zusatz-)Bibliothek verwenden.</p>
<p>Darüberhinaus haben die eingebauten Kalendersysteme dem JSR-310-Team bekannte Mängel, die bewußt in Kauf genommen werden. Beim Umalqra-Kalender fehlt die Möglichkeit, auch einen rein algorithmisch berechneten islamischen Kalender abzufragen. Im buddhistischen Thai-Kalender ist die Datierung vor 1940 (als der Beginn des Jahres von April in den Januar wanderte) nicht korrekt. Der japanische Kalender kann nur ab der Meiji-Ära benutzt werden. Von Oracle angedachte zukünftige Kalender wie der hebräische Kalender oder leider auch heute schon der Umalqura-Kalender unterstützen nicht den korrekten Beginn des islamischen Tages am Vortag zum Sonnenuntergang und liefern daher teilweise falsche Datumsangaben, wenn sie etwa in ChronoLocalDateTime zusammen mit einer Uhrzeit verwendet werden. Verblüffenderweise fehlt im JSR-310 auch ein historischer gregorianischer Kalender mit einer variablen cut-over-Regel wie in java.util.GregorianCalendar, von diversen anderen Lücken in historischen Datumsangaben zu schweigen (Beginn des historischen Jahrs &#8211; in England bis 1752 war es der 25. März, in Schweden gab es einmal den 30. Februar etc).</p>
<h1>Design-Mangel 5: Einschränkungen in der Dauer-Arithmetik</h1>
<p>Jedem Anwender wird die Trennung zwischen den Klassen Period und Duration etwas merkwürdig vorkommen. Ursprünglich war die Trennung durch die Unterscheidung zwischen &#8220;Human time-scale&#8221; und &#8220;Machine time-scale&#8221; motiviert, mittlerweile sagt der JSR-310, daß Period datumsbezogen (basiert auf Jahren, Monaten und Tagen) und Duration zeitbezogen (also mit Stunden, Minuten und Sekunden) arbeiten. Das hat schon so manchen Entwickler enttäuscht, die auch eine kombinierte Dauer à la 2 Tage und 12 Stunden erwartet hatten. Würde der JSR-310 letzteres unterstützen, müßte er entweder java.time.Period erweitern oder eine dritte Dauer-Klasse einführen. Wie auch immer, dann wird die Trennung zwischen den Dauer-Klassen zunehmend unverständlicher. Eine Trennung wäre höchstens noch sinnvoll, wenn etwa java.time.Duration ausschließlich mit SI-Sekunden im Gegensatz zu einer ExtendedPeriod-Klasse mit &#8220;normalen&#8221; Sekunden arbeiten würde. Aber das verbietet sich ja wegen der völlig diffusen Definition der &#8220;Java time-scale&#8221;.</p>
<p>Und dann gibt es noch das weitere von JodaTime geerbte Ärgernis, daß es immer noch möglich ist, eine Dauer mit gemischten Vorzeichen in den einzelnen Elementen zu erzeugen, zum Beispiel +2 Jahre und -5Monate (in der Notation des JSR-310: P2Y-5M). Das ist mit dem XML-Schema-Element xsd:Duration unvereinbar. Es führt auch zu einem nicht mehr behebbaren Fehler in der Definition der Methode Period#isNegative(), denn P1M-30D ist real nicht ohne weiteres negativ (tatsächlich nicht mehr vernünftig zu definieren). Schlimmer noch: Eine solche Definition einer &#8220;Periode&#8221; führt auch zu gewissen Inkonsistenzen in der Dauer-Arithmetik, die ausgesprochen unschön sind. Zum Beispiel würde es möglich sein, daß angewandt auf den ersten! Kalendertag eines Monats das Addieren und anschließende Subtrahieren derselben Dauer/Periode ein verändertes Datum herauskommt. Als Umgehungslösung kann man jedem Anwender nur davon abraten, überhaupt solche gemischte &#8220;Dauer&#8221;-Objekte zu erzeugen. Die einzig richtige Lösung wäre die, das Vorzeichen einer Dauer ausschließlich auf die gesamte Dauer zu beziehen, also z.B. -P2Y5M. Auch hier zeigt sich die unselige Tendenz des JSR-310, eigene Standards einzuführen, statt vorhandene Standards zu modellieren.</p>
<h1>Design-Mangel 6: Unsaubere Trennung zwischen Spec und Implementierung</h1>
<p>Wenn nur das java.time.temporal-Paket als Spezifikation interpretiert wird, fällt auf, daß z.B. das Interface TemporalUnit die Implementierungsklasse java.time.Duration referenziert. Oder die Hilfsklasse TemporalQueries referenziert Klassen wie Chronology, LocalDate etc. Das hat Werner Keil, Mitglied im JCP-Exekutiv-Komitee richtig beobachtet. Das JSR-310-Team definiert in einer Reaktion auf diese Kritik nun das gesamte API (bzw. die komplette Javadoc) als Spezifikation. Beide Ansätze verhindern, daß eine andere Bibliothek die Chance hat, sich als Implementierung des JSR-310 zu profilieren. Meine Bibliothek Time4J wird daher höchstens prüfen, ob das Interface TemporalAccessor zur Verbesserung der Interoperabilität nützlich sein kann (im Prinzip ja, aber was soll passieren, wenn der JSR-310 in zukünftigen ME-mobile editions nicht enthalten ist?).</p>
<h1>Design-Mangel 7: Die Klasse ZonedDateTime</h1>
<p>Es war schon in der alten JDK-Klasse GregorianCalendar gefährlich, lokale und globale Aspekte der Zeitarithmetik zu mischen. Die Motivation war und ist verständlich, alles möglichst mit einem einheitlichen Universaltyp zu behandeln. Aber der Preis dafür erscheint mir zu hoch. Wenn z.B. Tage addiert werden, so geschieht das auf der lokalen Zeitachse, die Addition von Stunden aber auf der globalen Zeitachse. Das ist durchaus willkürlich zu nennen (DST-Effekte mal ja, mal nicht einrechnen).</p>
<p>Auch ist die Sortierung und Vergleichbarkeit solcher Universaldatentypen schwierig zu definieren, wenn unterschiedliche Zeitzonen vorliegen. Auf Intuition kann der Standard-Anwender hier nicht mehr bauen.</p>
<p>Noch problematischer ist so ein Datentyp in der Deserialisierung, wenn sich die Zeitzonenregeln in der Zwischenzeit geändert haben. Ich habe hierzu leider keine brauchbare Dokumentation in der Klasse ZonedDateTime gefunden. Es wäre auch eine sehr komplizierte Dokumentation.</p>
<p>Mit anderen Worten: Eine Beschränkung der Klasse ZonedDateTime auf das Interface TemporalAccessor statt Temporal wäre weise gewesen, so daß alle zeitachsenbezogenen Methoden wie Addition, Subtraktion und Berechnung einer Dauer nicht angeboten werden. Das ist letztlich eine Konsequenz aus der Tatsache, daß es keine eindeutige Zeitachse gibt (viele lokale und eine globale Zeitachse gleichzeitig).</p>
<h1>Weitere Ärgernisse</h1>
<h2>a) Uhrzeit 24:00</h2>
<p>Überhaupt nimmt es der JSR-310 nicht so ganz genau mit der Beachtung externer Standards. Ein Beispiel ist jüngstens die Meldung einiger Beobachter, daß die Klasse LocalTime nicht den in der ISO-8601-Norm erlaubten Wert T24:00 (Mitternacht zum Ende eines Tages) unterstützt. Lediglich das Format-API bietet minimale und sehr umständliche Unterstützung beim Parsen solcher Angaben.</p>
<h2>b) ISO-Klassen mit Ära-Konzept</h2>
<p>Siehe <a href="http://www.menodata.de/blog/2012/11/09/hat-ein-iso-8601-datum-eine-gregorianische-ara/" target="_blank">den alten Artikel</a> in diesem Blog. Das Gefährliche daran ist, daß die Äras CE und BCE eigentlich nur andere Namen für AD und BC darstellen, hier aber in einem ahistorischen Kontext ohne einen Wechsel zwischen julianischen und gregorianischen Kalenderregeln auch zu anderen Datumsangaben führen können.</p>
<h2>c) Konkrete Adjuster und Queries nur statisch eingebaut</h2>
<p>Siehe die Klassen java.time.temporal.TemporalQueries oder java.time.temporal.TemporalAdjusters. Alle Methoden sind nur statisch. Das ist kein OO-Design und einfach nur ungeschickt. Viel besser wäre es gewesen, diese API-Elemente an den einzelnen Feldinstanzen aufzuhängen. Das hätte auch Raum für schöne Erweiterungen in der Zukunft gegeben. Ein Beispiel:</p>
<pre>// Statisch im JSR-310
LocalDate date = LocalDate.of(2013, 5, 10);
date = date.with(TemporalAdjusters.lastDayOfMonth());
System.out.println(date); // 2013-05-31

// OO-Ansatz
LocalDate date = LocalDate.of(2013, 5, 10);
date = date.with(DAY_OF_MONTH.maximized());
System.out.println(date); // 2013-05-31</pre>
<h2>d) Keine Intervall-Unterstützung</h2>
<p>Ursprünglich bis ca. 2009 immer erwähnt, ist das ab 2010 mit der lapidaren Begründung weggefallen, das sei nicht im Fokus des JSR-310. Eine bittere Enttäuschung auch für so manchen Business-Entwickler. Gerade im Versicherungs- und Bankengewerbe sind Intervalle wichtig, kann ich aus eigener Erfahrung sagen.</p>
<h2>e) Keine Schnittstelle zu Wochenenden und Feiertagen</h2>
<p>Auch das dürfte viele Business-Entwickler enttäuschen. Eine externe Bibliothek ist gezwungen, nicht nur die Feiertage zu definieren, sondern auch selbst die Arithmetik eines Business-Kalenders zu bauen, anstatt sie dem JDK zu überlassen. Schade.</p>
<h2>f) Das Format-API ist beim Parsen direkt nicht wirklich benutzbar.</h2>
<p>Der Parse-Vorgang erzeugt zunächst nur ein TemporalAccessor-Objekt. Dieses muß erst als Argument einer hoffentlich vorhandenen statischen from()-Methode im erwarteten Datentyp (z.B. LocalDate) zugewiesen werden, um das eigentliche Ergebnis zu bekommen. Das ist eine erhebliche Umgewöhnung im Vergleich zum alten SimpleDateFormat.</p>
<h2>g) Seltsame Felder und Einheiten, die wohl niemand braucht</h2>
<p>Eine Auswahl in den Klassen ChronoField und ChronoUnit: ALIGNED_WEEK_OF_YEAR (nicht zu verwechseln mit WeekFields#weekOfYear()!), FOREVER etc.</p>
<h2>h) LocalDate ohne DateResolver</h2>
<p>Gab es mal im ersten Entwurf und meint die unterschiedlichen Möglichkeiten, zu einem Datum Tage zu addieren &#8211; unter der Berücksichtigung der Monatslänge. Ist im Zuge der API-Verschlankung leider rausgefallen.</p>
<h2>i) IsoFields versus WeekFields</h2>
<p>Teilweise liegen redundante Felder vor &#8211; etwa WEEK_OF_WEEK_BASED_YEAR (außerdem ein sehr häßlicher und holpriger Name). Zudem hätte die im Unterpaket temporal versteckte Klasse WeekFields besser in das Hauptpaket java.time gepasst, weil gerade US-Entwickler nicht die ISO-Definition einer Kalenderwoche brauchen und daher ständig die Wochendefinition anpassen müssen.</p>
<h2>j) Zeitzonen-IDs im Olson-Format liegen nicht als Enums vor</h2>
<p>Eine frühe JavaOne-Foliensammlung versprach noch Schutz gegen Tippfehler wie &#8220;Asia/Hongkong&#8221; (ohne korrekten Unterstrich). Hier hätte ein Enum für Standard-IDs geholfen. Gibt es aber nicht.</p>
<h2>k) Clock als abstrakte Klasse statt als Interface</h2>
<p>Das ist auch ärgerlich, weil so externe Implementierungen nur erschwert werden. Noch schlechter: Die Klasse Clock schleppt mit einem eigenen Zeitzonenattribut unnötigen Ballast mit sich herum, der nicht in unmittelbarem Zusammenhang mit der Hauptmethode von Clock steht, nämlich einen Instant für die aktuelle Zeit zu liefern. Nicht jede konkrete Clock müßte eine Zeitzone definieren.</p>
<p>Auch ist der Zugang zur aktuellen Zeit oft nicht nur über die Clock-Klasse erhältlich, sondern auch über now()-Methoden in den Klassen von LocalDate etc. Es ist wegen der impliziten Verwendung von System-Einstellungen nicht so klar, was der Anwender letztlich bekommt (welche System-Zeitzone, welche Uhr?). NodaTime hat diesen Ansatz dankenswerterweise nicht. Ich empfehle, für die aktuelle Zeit entgegen allen &#8220;Bequemlichkeitsansätzen&#8221; lieber direkt mit der Clock-Instanz anzufangen und sich dann über den Instant bis zur Datumsklasse durchzuhangeln, wenn der heutige Tag gefragt ist.</p>
<p><strong>Fazit:</strong> Vieles ist gut, könnte aber viel besser sein. Die meisten möglichen Verbesserungen werden aber nie kommen, das kann man wohl schon jetzt sagen. Letztlich je nach Perspektive <span style="text-decoration: underline;">voll befriedigend</span> (dann ist der JSR-310 ein guter Ersatz für sowohl Joda Time als auch teilweise die alten JDK-Zeitklassen) bis <span style="text-decoration: underline;">Mittelmaß</span> (dann kann meine Bibliothek Time4J als vollwertiger Ersatz helfen, die Lücken zu stopfen &#8211; ich kämpfe noch darum, das Ding bis Ende des Jahres in einer ersten alpha-Version auszuliefern).</p>
]]></content:encoded>
			<wfw:commentRss>http://www.menodata.de/blog/2013/12/02/die-neue-zeitbibliothek-von-java8-eine-rezension/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Kleine Verzögerung</title>
		<link>http://www.menodata.de/blog/2013/09/18/kleine-verzogerung/</link>
		<comments>http://www.menodata.de/blog/2013/09/18/kleine-verzogerung/#comments</comments>
		<pubDate>Wed, 18 Sep 2013 05:36:10 +0000</pubDate>
		<dc:creator>Meno Hochschild</dc:creator>
				<category><![CDATA[Time4J]]></category>

		<guid isPermaLink="false">http://www.menodata.de/blog/?p=162</guid>
		<description><![CDATA[Seit einiger Zeit nichts mehr geschrieben, die Sommerpause war lang. Das heißt aber nicht, daß ich untätig war. Einige extreme private Umstände in meinem Umfeld (neues Baby, Krankenhausaufenthalte etc.) haben dazu geführt, daß ein Ein-Mann-Projekt wie Time4J nicht mit den &#8230; <a href="http://www.menodata.de/blog/2013/09/18/kleine-verzogerung/">Weiterlesen <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Seit einiger Zeit nichts mehr geschrieben, die Sommerpause war lang. Das heißt aber nicht, daß ich untätig war. Einige extreme private Umstände in meinem Umfeld (neues Baby, Krankenhausaufenthalte etc.) haben dazu geführt, daß ein Ein-Mann-Projekt wie Time4J nicht mit den gleichen hohen Anstrengungen wie vorher weitergeführt werden konnte. Konkret bedeutet das erst mal nur, daß das erste Release nicht wie angekündigt im Spätsommer/Herbst erscheinen kann. Ich hoffe aber immer noch, daß es spätestens im Dezember klappt, wenn auch nur mit einem an Features reduzierten Release.</p>
<p>An der Tatsache, daß ich leider nur relativ wenig Zeit in das Projekt investieren kann, wird sich wegen meiner privaten Situation so schnell nichts ändern. Darum werde ich auch demnächst wenig bis gar nichts bloggen, sondern lieber meine wenige vorhandene Zeit in Time4J stecken so gut es geht. Eine Ausnahme werde ich aber machen, wenn der &#8220;Public Draft Review&#8221; (PDR) des JSR 310 erscheint. Die neue Zeitbibliothek des JDK von Oracle und Stephen Colebourne steht kurz vor dem Abschluß. Dazu werde ich eine ausführliche Stellungnahme im Blog abgeben, fest versprochen.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.menodata.de/blog/2013/09/18/kleine-verzogerung/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Die Mühsal mit java.util.Date &amp; Co</title>
		<link>http://www.menodata.de/blog/2013/06/21/die-muhsal-mit-java-util-date/</link>
		<comments>http://www.menodata.de/blog/2013/06/21/die-muhsal-mit-java-util-date/#comments</comments>
		<pubDate>Fri, 21 Jun 2013 04:43:22 +0000</pubDate>
		<dc:creator>Meno Hochschild</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Altersberechnung]]></category>
		<category><![CDATA[java.uti.Date]]></category>
		<category><![CDATA[java.util.GregorianCalendar]]></category>

		<guid isPermaLink="false">http://www.menodata.de/blog/?p=156</guid>
		<description><![CDATA[Eine Weile  her hatte ich mich im deutschen Java-Forum für Anfänger in eine Debatte über die Altersberechnung eingeschaltet. Die Frage zielte darauf ab, wie ausgehend vom Geburtsdatum (gegeben durch Jahr, Monat und Tag als Strings) das Alter einer Person berechnet &#8230; <a href="http://www.menodata.de/blog/2013/06/21/die-muhsal-mit-java-util-date/">Weiterlesen <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Eine Weile  her hatte ich mich<a href="http://www.java-forum.org/java-basics-anfaenger-themen/150489-erstellen-konstruktoren-berechnung-alter-jahren.html" target="_blank"> im deutschen Java-Forum für Anfänger</a> in eine Debatte über die Altersberechnung eingeschaltet.</p>
<p>Die Frage zielte darauf ab, wie ausgehend vom Geburtsdatum (gegeben durch Jahr, Monat und Tag als Strings) das Alter einer Person berechnet werden kann. Es klingt einfach, wenn die richtigen Bibliotheken verwendet werden. Alle folgenden Code-Listings entsprechen nicht exakt dem Debattenstand, verfälschen aber nicht die algorithmischen Grundideen. <span id="more-156"></span></p>
<p>Bemerkenswerterweise hat ein Vielschreiber im Forum trotz aller Gegenargumente verbissen an folgendem Lösungsvorschlag festgehalten, der wahrscheinlich und unglücklich so auch vom Fragesteller akzeptiert wurde (letzterer hatte die Debatte nicht mehr weiter verfolgt).</p>
<pre>package net.time4j.experiment;</pre>
<pre> import java.text.DateFormat;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.Calendar;
 import java.util.Date;
 import java.util.GregorianCalendar;

 public class Altersberechnung1 {
   public static void main(String[] args) throws ParseException {
     SimpleDateFormat sdf = (SimpleDateFormat) DateFormat.getDateInstance();
     Date geb = sdf.parse("19.06.1970");
     int a = -1;
     GregorianCalendar gebc = new GregorianCalendar();
     gebc.setTime(geb);
     GregorianCalendar nowc = new GregorianCalendar();

     while (gebc.before(nowc)) {
       a++;
       gebc.add(Calendar.YEAR, 1);
     }
     System.out.println("Alter1: " + a);
  }
}</pre>
<p>Hier gibt es mehrere Probleme, siehe die nachfolgenden Code-Kommentierungen:</p>
<pre>package net.time4j.experiment;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

public class Altersberechnung2 {

  public static void main(String[] args) throws ParseException {
    System.out.println("Alter2: " + alter("1972", "02", "29")); // 40 falsch
    System.out.println("Alter2: " + alter("1972", "02", "28")); // 40 richtig
  }

  private static int alter(
    String year,
    String month,
    String day
  ) throws ParseException {
    SimpleDateFormat sdf = (SimpleDateFormat) DateFormat.getDateInstance();
    // Verstecktes Problem:
    // Implizite Zeitzonenabhängigkeit hat Auswirkung auf Date-Vergleich
    Date geb = sdf.parse(day + "." + month + "." + year);

    int a = -1;
    GregorianCalendar gebc = new GregorianCalendar();
    gebc.setTime(geb);
    GregorianCalendar nowc = new GregorianCalendar();

    // Testbeispiel: HEUTE = 1 Sekunde nach Mitternacht am 28. Februar 2012
    Date now = new Date(sdf.parse("28.02.2012").getTime() + 1000);
    nowc.setTime(now);

    System.out.println(gebc.getTime());
    System.out.println(nowc.getTime());

    while (gebc.before(nowc)) { // zu Mitternacht fehlerhafter Vergleich
      a++;
      gebc.add(Calendar.YEAR, 1); // macht aus 29.2. immer 28.2 =&gt; Fehler
    }
    return a;
  }

}</pre>
<p>Ein anderer Forenteilnehmer hatte diesen zählenden Algorithmus sogar als professionell bezeichnet, war aber in Wahrheit wohl eher von der Komplexität der Lösung beeindruckt. Seine einfache, aber weitaus bessere Idee (mit einer kleinen Korrektur meinerseits) sah so aus:</p>
<pre>package net.time4j.experiment;

import java.util.GregorianCalendar;

public class Altersberechnung3 {

  public static void main(String... args) {
    System.out.println("Alter3: " + alter("1972", "02", "29")); // 39 richtig
    System.out.println("Alter3: " + alter("1972", "02", "28")); // 40 richtig

    // vereinfachter Performance-Test (grob)
    long start = System.nanoTime();
    for (int i = 0; i &lt; 1000; i++) {
      alter("1972", "02", "29");
    }
    long end = System.nanoTime() - start;
    System.out.println(end); // ~ 0.03sec

  }

  private static int alter(String year, String month, String day) {
    // Heute-Datum bestimmen
    GregorianCalendar gcal = new GregorianCalendar(); // Zeitfresser!
    int tagheute = 28; //gcal.get(Calendar.DAY_OF_MONTH);
    int monatheute = 2; // gcal.get(Calendar.MONTH) + 1;
    int jahrheute = 2012; // gcal.get(Calendar.YEAR);

    int geburtstag = Integer.parseInt(day);
    int geburtsmonat = Integer.parseInt(month);
    int geburtsjahr = Integer.parseInt(year);

    if (
      (tagheute&gt;=geburtstag &amp;&amp; geburtsmonat==monatheute)
      ||(monatheute&gt;geburtsmonat)
    ) {
      return jahrheute-geburtsjahr;
    } else {
      return jahrheute-geburtsjahr-1;
    }
  }

}</pre>
<p>Hieran besticht schon die Einfachheit. Das einzige Problem damit ist lediglich ein gewisser Mangel an sprachlicher Flüssigkeit (fluent programming style). Trotzdem hatte ich dem Vielschreiber noch eine korrigierte Version seines Zählalgorithmus präsentiert.</p>
<pre>package net.time4j.experiment;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;

public class Altersberechnung4 {

  public static void main(String[] args) throws ParseException {
    System.out.println("Alter4: " + alter("1972", "02", "29")); // 39 richtig
    System.out.println("Alter4: " + alter("1972", "02", "28")); // 40 richtig
  }

  private static int alter(
    String year,
    String month,
    String day
  ) throws ParseException {
    // Format ohne Uhrzeit =&gt; getDateInstance()
    SimpleDateFormat sdf = (SimpleDateFormat) DateFormat.getDateInstance();
    sdf.setTimeZone(utc());
    Date geb = sdf.parse(day + "." + month + "." + year);
    Date now = sdf.parse("28.02.2012");

    // vereinfachter Performance-Test nur für den Zählalgorithmus (grob)
    // SimpleDateFormat-parse() ist hier beschönigend nicht mitgerechnet!
    long start = System.nanoTime();
    for (int i = 0; i &lt; 1000; i++) {
      alter(geb, now);
    }
    long end = System.nanoTime() - start;
    System.out.println(end); // &gt; 0.5sec

    return alter(geb, now);
  }

  private static int alter(Date geb, Date now) throws ParseException {
    int a = -1;

    GregorianCalendar gebc = createCalendar();
    gebc.setTime(geb);

    GregorianCalendar nowc = createCalendar();
    nowc.setTime(now);

    //        System.out.println(gebc.getTime());
    //        System.out.println(nowc.getTime());
    //        System.out.println("Hour: " + gebc.get(Calendar.HOUR_OF_DAY));

    boolean schalttag =
      (gebc.get(Calendar.MONTH) == Calendar.FEBRUARY)
      &amp;&amp; (gebc.get(Calendar.DAY_OF_MONTH) == 29);

    while (!gebc.after(nowc)) {
      a++;
      gebc.add(Calendar.YEAR, 1);
      if (
        schalttag
        &amp;&amp; gebc.isLeapYear(gebc.get(Calendar.YEAR))
        &amp;&amp; (gebc.get(Calendar.DAY_OF_MONTH) == 28)
      ) {
        gebc.add(Calendar.DATE, 1);
      }
    }

    return a;
  }

  // synthetische Zeitzone ohne Sommerzeitumstellung
  private static GregorianCalendar createCalendar() {
    return new GregorianCalendar(utc());
  }

  private static TimeZone utc() {
    return TimeZone.getTimeZone("GMT+00:00");
  }
}</pre>
<p>Hätten Sie, verehrte Leser, jedes Detail auf Anhieb parat gehabt. Ich auch nicht. Ein nicht unheblicher Teil der Schwierigkeiten liegt letztlich daran, daß das JDK keinen Datentyp für ein reines Datum bietet. So müssen evtl. sogar Zeitzoneneffekte beachtet werden, obwohl das von der Fragestellung her nicht einleuchtet. Der Code sieht fürchterlich komplex aus, kostet erhebliche Entwicklerressourcen und ist voll potentieller Fallstricke, die zwar nur selten zuschlagen. Aber da halte ich es mit Murphy: Was schief gehen kann, wird in der Praxis auch schief gehen. Am problematischsten ist aktuell die Behandlung der Schalttagskinder.</p>
<p>Wie geht es nun aber am besten? Meiner Meinung nach mit Time4J (und so ähnlich auch mit dem neuen JSR 310):</p>
<pre>package net.time4j.experiment;

import net.time4j.IsoDate;
import static net.time4j.StdDateUnit.YEARS;

public class Altersberechnung5 {

  public static void main(String... args) {
    System.out.println("Alter5: " + alter("1972", "02", "29")); // 39 richtig
    System.out.println("Alter5: " + alter("1972", "02", "28")); // 40 richtig

    // vereinfachter Performance-Test (grob)
    long start = System.nanoTime();
    for (int i = 0; i &lt; 1000; i++) {
      alter("1972", "02", "29");
    }
    long end = System.nanoTime() - start;
    System.out.println(end); // ~ 0.06sec today() / 0.01sec festes Heute-Datum

  }

  private static long alter(String year, String month, String day) {
    int geburtstag = Integer.parseInt(day);
    int geburtsmonat = Integer.parseInt(month);
    int geburtsjahr = Integer.parseInt(year);

    IsoDate geb = new IsoDate(geburtsjahr, geburtsmonat, geburtstag);
    IsoDate now = new IsoDate(2012, 2, 28); // IsoDate.today();

    return YEARS.betweenDates(geb, now);
  }

}</pre>
<p>Mit <code>YEARS.betweenDates(geb, now)</code> ist de facto ein Einzeiler als Lösung möglich. Dazu ist die Lösung intuitiv und performant (mindestens ca. 10x so schnell wie der komplexe Zählalgorithmus vorgestellt im Listing Alterberechnung4). Zwar hatte der Vielschreiber mit Hinweis auf das begrenzte Lebensalter von Personen mein Performance-Argument für irrelevant erklärt, aber ich meine: Wenn z.B. in einer Personentabelle mit 1000 Datensätzen online für jede Zeile das Alter berechnet werden soll und wenn alleine dafür bei nicht optimaler Hardware mehr als eine halbe Sekunde verstreicht, dann ist das zuviel für den User, vor allem, wenn man bedenkt, daß diese Zeit extra zur Abfragezeit einer solchen Personentabelle kommt (meist werden User schon nach 2 Sekunden unruhig).</p>
<p>Hier sei noch ein Hinweis auf JodaTime gestattet. Diese Bibliothek erlaubt ein ähnlich kurzes und intuitives Programm zur Alterberechnung zu schreiben, berechnet aber die Differenz zwischen 1972-02-29 und 2013-02-28 fehlerhaft als 41 statt 40 Jahre, weshalb ich für JodaTime hier keine vollständig positive Empfehlung aussprechen möchte. Interessanterweise rechnet JodaTime die Differenz für ein Jahr früher richtig mit 39 Jahren aus, nämlich für 2012-02-28.</p>
<h2>Fazit:</h2>
<p>Die alten Klassen java.util.Date und Co. führen zu Horror-Code &#8211; und finden trotzdem ihre ergebenen Anhänger, die komplexes Programmieren mit gutem Programmieren verwechseln. Sie dürften trotz neuerer Bibliotheken wie dem kommenden JSR 310 noch sehr lange weiterleben, zumal Oracle sich nicht dazu durchringen kann, diese alten Klassen komplett für deprecated zu erklären oder noch besser, sie im Rahmen einer Modularisierungsstrategie ganz rauszunehmen.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.menodata.de/blog/2013/06/21/die-muhsal-mit-java-util-date/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>UI-Control für Datumseingaben</title>
		<link>http://www.menodata.de/blog/2013/04/19/ui-control-fur-datumseingaben/</link>
		<comments>http://www.menodata.de/blog/2013/04/19/ui-control-fur-datumseingaben/#comments</comments>
		<pubDate>Fri, 19 Apr 2013 19:44:31 +0000</pubDate>
		<dc:creator>Meno Hochschild</dc:creator>
				<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://www.menodata.de/blog/?p=153</guid>
		<description><![CDATA[Die Swing-Technologie für Java, um Desktop-Anwendungen mit einer anspruchsvollen Oberfläche zu erzeugen, ist inzwischen von Oracle mittelfristig abgekündigt. JavaFX gilt jetzt als der Nachfolger (und ich habe gehört, ca. 100 Entwickler lässt Oracle daran arbeiten!). Lohnt es sich noch, sich &#8230; <a href="http://www.menodata.de/blog/2013/04/19/ui-control-fur-datumseingaben/">Weiterlesen <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Die Swing-Technologie für Java, um Desktop-Anwendungen mit einer anspruchsvollen Oberfläche zu erzeugen, ist inzwischen von Oracle mittelfristig abgekündigt. JavaFX gilt jetzt als der Nachfolger (und ich habe gehört, ca. 100 Entwickler lässt Oracle daran arbeiten!). Lohnt es sich noch, sich mit Swing zu beschäftigen? Tatsächlich gibt es trotz bemerkenswerter Features in JavaFX noch mindestens eine UI-Komponente in Swing, die es bis jetzt leider nicht nach JavaFX hinein geschafft hat, nämlich die Klasse javax.swing.JFormattedTextField. Das ist sehr schade, handelt es sich doch um eine sehr mächtige Texteingabekomponente mit anspruchsvoller Anzeige- und Eingabe-Formatunterstützung. Ich konzentriere mich hier vor allem auf die Datumsformatunterstützung.<span id="more-153"></span></p>
<p>Was ist das für eine UI-Komponente? Dazu gibt es Java-Tutorials. Ich werde hier den Link zu <strong><a href="http://docs.oracle.com/javase/tutorial/uiswing/components/spinner.html" target="_blank">JSpinner</a></strong> angeben, das intern ein JFormattedTextField verwendet. Im ersten Bildbeispiel der Tutorial-Webseite sieht man u.a. ein Monat/Jahr-Eingabefeld mit Label &#8220;Another Date&#8221;. Interessierte sollten sich das Beispiel mit Java-Webstart runterladen, ausführen und ausprobieren. Wird z.B. der Text-Cursor auf den Monatsteil des Datums gesetzt, dann bewirken anschließend die Pfeiltasten rauf/runter das Inkrementieren oder Dekrementieren des Monatswerts. Wird der Cursor hingegen auf den Jahresteil gesetzt, werden die Pfeiltasten stattdessen den Jahreswert ändern. Mit anderen Worten: Mit Hilfe der vier Pfeiltasten links, rechts, rauf und runter kann ein beliebig!!! strukturiertes Datum gezielt und partiell verändert werden. Auch eine Addition von Kalenderwochen oder eine Änderung des Zeitzonen-Offsets ist so realisierbar.</p>
<p>Der JSpinner bietet darüberhinaus zwei kleine Buttons, die ähnlich wie die Pfeiltasten wirken, nur eben per Mausklick. Und wenn man sich dazu noch einen weiteren Button mit einer DatePicker-Popup-Komponente denkt, hat man wirklich die ultimative Datums- und Zeiteingabekomponente vorliegen. Maximaler Eingabe- und Anzeigekomfort per Tastatur oder Mausklick.</p>
<p>Bleibt noch die Frage, was eine Datums- und Zeitbibliothek leisten muß, um eine solche UI-Komponente zu unterstützen. Die technische Antwort lautet: Unterstützung für das java.text.AttributedCharacterIterator-Konzept. Der Datumswert muß nämlich während der Formatierung zu einem Text im UI-Control zugleich dem Control mitteilen, welcher Teil des Texts für welches Element im Datum steht. Oder anders ausgedrückt: Das Control muß wissen, von welchem Start-Index bis zu welchem End-Index z.B. ein Monatselement vorliegt. Genau das leistet das besagte AttributedCharacterIterator-Konzept, und zwar über die java.text.Format-Methode formatToCharacterIterator(). Beim Design des Format-APIs von Time4J, meiner kommenden Datumsbibliothek habe ich hierauf zu achten.</p>
<p>Wie sieht es damit im JSR-310 aus, der neuen Standardbibliothek von Java 8? Dazu hatte ich eine Frage auf deren <a href="http://sourceforge.net/mailarchive/forum.php?thread_name=5169AE5D.1080002%40gmx.de&amp;forum_name=threeten-develop" target="_blank">Mailing-Liste</a> gestellt. Die Antworten der Projektleiter sind bemerkenswert. Ich war erst mal sprachlos. Tatsächlich kennt der Oracle-Vertreter im JSR 310 noch nicht einmal diese Technik. Und auch sonst habe ich das Gefühl, daß niemand dort den technischen Hintergrund voll erfasst hat. Ich habe in meiner Antwort aus naheliegenden Gründen darauf verzichtet, Oracle seine alten, aber in diesem Fall exzellenten Produkte näher zu erklären. Es wird also im JSR-310 wohl dabei bleiben, daß wir in der JavaDoc zur neuen 310-Klasse DateTimeFormatter lesen müssen:</p>
<blockquote>
<pre>public java.text.Format toFormat()</pre>
<div>Returns this formatter as a <code>java.text.Format</code>instance.The returned <code>Format</code> instance will format any <a title="interface in java.time.temporal" href="http://cr.openjdk.java.net/%7Erriggs/javadoc-nopublicdefault-302/java/time/temporal/TemporalAccessor.html"><code>TemporalAccessor</code></a> and parses to a resolved <a title="interface in java.time.temporal" href="http://cr.openjdk.java.net/%7Erriggs/javadoc-nopublicdefault-302/java/time/temporal/TemporalAccessor.html"><code>TemporalAccessor</code></a>.</p>
<p>Exceptions will follow the definitions of <code>Format</code>, see those methods for details about <code>IllegalArgumentException</code> during formatting and <code>ParseException</code> or null during parsing.<strong> The format does not support attributing of the returned format string.</strong></p>
</div>
</blockquote>
<dl>
<dt>Returns:</dt>
<dd>
<blockquote><p>this formatter as a classic format instance, not null</p></blockquote>
</dd>
</dl>
<p>Ein Jammer, das JFormattedTextField zwar in Swing, aber bis jetzt noch nicht in JavaFX vorhanden ist. Ich glaube, sollte keine entsprechende Komponente in JavaFX bald kommen, werde ich eben selbst eine erstellen &#8211; mit extra Unterstützung für Time4J und durch Time4J. Nur meine aktuellen Arbeitskapazitäten verhindern das noch.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.menodata.de/blog/2013/04/19/ui-control-fur-datumseingaben/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Threeten/JSR 310 im compact-1-Profil des JDK 8</title>
		<link>http://www.menodata.de/blog/2013/03/22/threetenjsr-310-im-compact-1-profil-des-jdk-8/</link>
		<comments>http://www.menodata.de/blog/2013/03/22/threetenjsr-310-im-compact-1-profil-des-jdk-8/#comments</comments>
		<pubDate>Fri, 22 Mar 2013 20:50:10 +0000</pubDate>
		<dc:creator>Meno Hochschild</dc:creator>
				<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://www.menodata.de/blog/?p=150</guid>
		<description><![CDATA[Ich hatte früher noch geschrieben, daß keineswegs sicher ist, ob das Threeten-Projekt wirklich schon in trockenen Tüchern liegt. Auch dessen Projektleiter Stephen Colebourne hatte mal leichte Zweifel angemeldet. Zuletzt hat sogar ein Mitglied des Exekutivkomitees im JCP (Java Community Process) &#8230; <a href="http://www.menodata.de/blog/2013/03/22/threetenjsr-310-im-compact-1-profil-des-jdk-8/">Weiterlesen <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Ich hatte früher noch geschrieben, daß keineswegs sicher ist, ob das Threeten-Projekt wirklich schon in trockenen Tüchern liegt. Auch dessen Projektleiter Stephen Colebourne hatte mal leichte Zweifel angemeldet. Zuletzt hat sogar ein Mitglied des Exekutivkomitees im JCP (Java Community Process) Zweifel gehabt (Werner Keil), siehe auch seine Kommentare auf <a href="http://www.java-forum.org/allgemeine-java-themen/146248-jsr-310-api-weitgehend-fertig.html" target="_blank">java-forum.org</a> Begründet hat er das mit der Größe von Threeten, aber auch mit der seiner Meinung nach nicht so gelungenen Interoperabilität mit dem Rest des JDK.</p>
<p>Aber nun verdichten sich doch stark die Anzeichen, daß der JSR 310 nicht nur irgendwie ins JDK rutschen wird. Er ist jetzt entsprechend den Mailing-Listen des OpenJDK-Projekts ausdrücklich für die Basisversion des JDK vorgesehen, tatsächlich für das sogenannte compact profile No. 1, das auch andere Basispakete wie java.lang/java.io/java.util etc. enthält. Außerdem hat der JSR 310 mittlerweile ein relativ starkes Argument gegenüber Bedenken, die auf die Größe des Projekts hinweisen &#8211; im Hinblick auf die Eignung für kleine Mobilgeräte wie smart phones. Es ist mittlerweile beschlossene Sache, daß die alte Zeitzonendatenbank im Ordner lib/zi mit ihren vielen binär kodierten Zeitzonendateien durch eine viel schlankere tz-Datenbank als (komprimiertes) jar-Archiv ersetzt werden wird. Allein das spart so viel, daß die Größe des JSR 310 damit wohl genügend kompensiert wird.</p>
<p>Wir werden also in Zukunft eine im Vergleich zu den alten Datums- und Kalenderklassen stark verbesserte Zeitbibliothek sehen. Und wenn dann noch meine externe Time4J-Bibliothek für High-End-Ansprüche im Spätsommer dazukommt, wird Java in Sachen Datum und Zeit richtig gut im Vergleich zu seinen Mitbewerbern à la .NET, PHP, Perl etc. Freuen wir uns. <strong>Java auf der Überholspur.</strong></p>
]]></content:encoded>
			<wfw:commentRss>http://www.menodata.de/blog/2013/03/22/threetenjsr-310-im-compact-1-profil-des-jdk-8/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
