Probleme mit Threeten => Time4J

Wie in der zuletzt am 27.9.2012 aktualisierten Übersicht von Datums- und Zeitbibliotheken ersichtlich, ist derzeit die für das JDK 8 vorgesehene Bibliothek Threeten (JSR 310) die am besten zu bewertende. Joda habe ich in jenem Artikel nur deshalb empfohlen, weil sich Threeten noch im alpha-Stadium befindet, also ein (sehr) instabiles API hat.

Um Mißverständnissen bezüglich der weiter unten vorgebrachten Kritiken vorzubeugen, sei eins vorausgeschickt. Zweifelsohne ist Threeten für alle Standardanwendungen in Sachen Zeit meist gut geeignet und in der Mehrzahl der Fälle klar besser als die alten JDK-Klassen, auch besser als Joda. Stephen Colebourne, die treibende Kraft hinter Joda und Threeten, hat über die Jahre wirklich enorme Fleißarbeit verrichtet. Inzwischen gibt es auch einen zweiten Early Draft Review (EDR2) des JSR 310 (Version 0.7). Hier zeigt sich allerdings auch schon die Instabilität des ganzen API, denn wenn man stattdessen direkt in den Quelldateien auf GitHub stöbert, stellt man erhebliche Abweichungen des aktuellen Quellcode-Stands vom EDR2 fest. Es ist also etwas schwierig, alleine auf Basis des EDR2 Bewertungen vorzunehmen. Vieles ist teilweise noch stark im Fluß…

A. Der JSR-310-Draft-Guide

Nach der üblichen Einführung, was überhaupt die Grundelemente der API-Klassen sind, wird in Abschnitt 2 eine Erläuterung der im JSR-310 verwendeten Terminologie gebracht. Vieles daran ist gut und zu loben, auch deshalb, weil vielen Menschen die kalendarischen Grundbegriffe nicht klar sind. Aber eine Sache fällt leider negativ auf. Ein Großteil des Abschnitts 2 (ca. 16% des gesamten Artikels!) beschäftigt sich wortreich mit der UTC-Definition und Schaltsekunden, um am Ende implizit nur zu sagen, daß UTC-Schaltsekunden nicht unterstützt werden. Das erinnert mich an den Spruch: Der Berg kreißte und gebar eine Maus. Übrigens hat die überaus wortreiche, aber im Ergebnis überflüssige Auseinandersetzung mit dem UTC-Standard bei nicht wenigen im Oracle-Umfeld falsche Erwartungen geweckt. Wie auch immer, eine echte vollwertige UTC-Unterstützung inklusive Schaltsekunden wird in Threeten nicht kommen. Das ist nach langen Debatten in Stein gemeißelt. Sicherlich ist das für Standardanwendungen in der Praxis kein wichtiges Feature, aber aus wissenschaftlicher Perspektive ist diese Entscheidung schon ärgerlich, und schlimmer noch: Das Feature läßt sich ohne Design-Änderungen und damit Brüche in der Rückwärtskompatibilität nicht mehr nachrüsten.

Die in Abschnitt 3 erwähnten Design-Ziele >>Immutable<<, >>Fluent API<<, >>Clear, Explizit and Expected<< und  >>Extensible<< klingen sehr gut. Die ersten zwei Punkte kann man schon bei flüchtiger Betrachtung des aktuellen API sofort unterschreiben. Der dritte Punkt ist leider nicht mehr so offensichtlich. Denn im Bemühen, ein schlankeres kleineres API zu schaffen, haben die Projektmitglieder von Threeten u.a. die Typsicherheit bei Additionen (bewußt) über Bord geworfen. Zum Beispiel ist es möglich, zu einem reinen Datum (LocalDate) eine Anzahl von Minuten zu addieren. Der Compiler läßt es durchgehen, aber zur Laufzeit fliegt eine Ausnahme. Dieses Detail ist unter anderem der mangelnden Typisierung und damit mangelnden Klarheit der verwendeten Zeiteinheiten zuzuschreiben. Der Mangel an Typsicherheit in Threeten ist leider allgemein und überall im API anzutreffen. Mit einem überlegten Einsatz von Java-Generics hätte das besser gelöst werden können, erfordert aber einen kompletten Neuentwurf.

Auch das vierte Design-Ziel der Erweiterbarkeit (>>Extensible<<) trifft nur bedingt zu. Richtig ist es auf der Low-Level-API-Ebene der Zeitfelder und Zeiteinheiten, wo man als Entwickler auf den jeweiligen Datums- oder Zeittyp individuelle Felder und Einheiten mit einem eigenen integrierten Regelwerk anwenden kann. Ein tolles Feature! Eine weitere brillante Idee aus der OO-Design-Welt ist die Einführung eines DateTimeAdjuster-Interface, auch wenn es für meinen Geschmack manchmal übertrieben wird.

Aber im High-Level-Bereich für I18N-Anwendungen (javax.time.chrono-Paket) ist die Erweiterbarkeit eine Farce, sobald versucht wird, ein neues Kalendersystem nicht auf Jahr/Monat/Tag-Basis zu bauen (z.B. Maya-Kalender, GPS-Zeit etc.). Es sei hier ausdrücklich erwähnt, daß das I18N-Design von allgemeinen Kalendersystemen zur Zeit im Projektteam umstritten ist. Während Stephen Colebourne auf dem aktuellen Design beharrt und nicht an eine gemeinsame abstrakte Skelettimplementierung einschließlich der ISO-Klassen glaubt (siehe Wiki auf GitHub), wollen nicht wenige von Oracle (Roger Riggs, D. Chiba et. al.) in eine ganz andere Richtung. Ich habe den Eindruck, Oracle wäre mit einer Art >>immutable<< Neuauflage des java.util.Calendar-Designs zufrieden, siehe auch die entsprechende Debatte auf GitHub. Beide Seiten haben in dieser Debatte ihre berechtigten Argumente. Eine von Oracle gewünschte Vereinheitlichung der Kalendersysteme mit einer abstrakten Basisklasse ist im I18N-Umfeld sehr sinnvoll. Umgekehrt ist Colebournes Argument nicht von der Hand zu weisen, wenn Entwickler versuchen, internationale non-ISO-Kalender wie ISO-Kalender zu behandeln (was zu versteckten schwer zu findenden Fehlern in Anwendungen führt – z.B. falsche Erwartungen an die Zahl der Tage im Monat etc.). Ein Ausweg aus dem Dilemma wäre nur, ein abstraktes Kalendersystem zu konstruieren, das kein Jahr/Monat/Tag-Design voraussetzt, somit keine Behandlung als konkretes ISO-System erlaubt, sondern eine andere abstrakte Herangehensweise erzwingt, was wiederum mit Java-Generics machbar erscheint, aber offenbar kein an der Debatte Beteiligter auf dem Zettel hat.

Die im Abschnitt 4 enthaltene Diskussion um die Trennlinie zwischen “Machine-scale time” und “Human-scale time” halte ich für höchst fragwürdig. Letztlich ist ja auch eine reine Sekundenorientierung auf einer Zeitachse (die Maschinenskala) eine von Menschen gemachte Definition. Die Trennlinie wird hier eher mit dem Kriterium begründet, welche Zeiteinheiten im zivilen Alltag präsenter sind. Es handelt sich mathematisch gesehen tatsächlich nur um zwei verschiedene Sichten auf die gleiche (UTC-)Zeitachse. Ein Ergebnis der Trennung zwischen Maschinen- und Menschensicht ist im API die Existenz zweier getrennter Dauer-Klassen, nämlich Duration und ISOPeriod. Schon in Joda-Time ist dieses Nebeneinander eine latente Quelle der Verwirrung bei Anwendern. Dabei läßt sich javax.time.Duration eigentlich als ein Spezialfall von javax.time.ISOPeriod interpretieren (nämlich auf Sekunden begrenzt). Ich halte diese Art von Trennlinie für überflüssig, wenn es gelingt, eine generische Dauer mit einem per Generics zu spezifizierenden Typparameter für Zeiteinheiten einzuführen.

B. Weitere API-Betrachtungen

Auch das arithmetische Konzept der zeitlichen Dauer (ISOPeriod) erscheint mir unausgegoren, besonders die Art und Weise, wie eine negative Dauer definiert wird. Tatsächlich gibt es in Threeten nicht wirklich eine negative Dauer, sondern nur Dauer-Objekte mit negativen Beträgen, ein subtiler Unterschied, der sich in der Formatierung P-2D statt -P2D niederschlägt (nicht XML-konform). Wäre es nur ein reines Formatierungsproblem, so könnte man damit noch leben. Leider sind die Konsequenzen weitreichender. Mit der Threeten-Semantik (die so bruchlos aus Joda-Time übernommen wurde) ist es nicht mehr möglich, eine Dauer insgesamt als negativ oder positiv zu qualifizieren, denn was soll z.B. P-1M30D (-1 Monat + 30 Tage) sein, positiv, leer oder negativ? Damit ist die von Anwendern erwartete und selbstverständlich erscheinende Invarianzbedingung Dauer(start, ende) = -Dauer(ende, start) nicht mehr haltbar. Die Addition und Subtraktion von Dauern und Zeitpunkten kann somit nicht mehr vollständig konsistent sein. Roger Riggs von Oracle hat die für die Arithmetik negativen Konsequenzen in eigenen gründlichen Tests schon gesehen. Aber Colebourne möchte die Klasse ISOPeriod mitsamt seiner leicht beschädigten Dauer-Arithmetik auf jeden Fall durchsetzen. Ein auch für Standard-Zeitanwendungen beunruhigender Aspekt!

Ebenso sind die Low-Level-API-Interfaces “DateTime” und “AdjustableDateTime” mangels self-referencing-generics de facto unbrauchbar, zwingen ihre Methodensignaturen doch Entwickler, in Subklassen die Methodenrückgabetypen jedes Mal durch kovariante Return-Types einzuengen. Mich wundert generell, daß in all den Jahren (Java 5 ist schon lange her, und der JSR 310 existiert auch schon mehr als 5 Jahre) eine Generics-Strategie zur Steigerung der Typsicherheit und der Erweiterbarkeit des API niemals ernsthaft verfolgt wurde. Und Colebourne wurde darauf mehrmals in seinem eigenen Blog hingewiesen, aber es kommt keine Reaktion außer, daß er in Java das fehlende Sprach-Feature von sogenannten self-types beklagt (und das auch im Jahr 2012 immer noch tut).

Nachteilig ist in diesem Zusammenhang wohl die Tatsache, daß die personelle Projektstruktur von Threeten de facto nur auf eine Person hin ausgerichtet ist. Während Colebourne fast alles selber codiert und seine Design-Ideen so maßgeblich einbringt und meistens durchsetzt, kommt aus der Java-Community herzlich wenig. Und nur Debattenbeiträge oder Anregungen per Mail aus der Community alleine ändern noch nicht den Gesamtkurs. Wie sagte doch der früher am JSR 310 beteiligte Projektleiter Michael Nascimento Santos zum Projektbeginn 2007 weise z.B. über die Realisierung eines UTC-Schaltsekunden-Features: “…this is the kind of issue that requires working code to be analyzed [...] but without a UTC, no leap-second support implementation as a basis for comparison, I think it is hard to evaluate the situation right.” Also eine Code-Alternative kann manchmal mehr bewegen als noch soviele Worte… Aber die Community liefert bis jetzt keine Alternativen, sondern wartet fast wie das Kind auf den Weihnachtsmann ;-)

C. Zusammenfassung – und eine Ankündigung

Threeten ist gut und bringt viele Verbesserungen, aber ist weit davon entfernt, eine High-End-Bibliothek zu sein.

Ich habe die Debatten in und um Threeten in den letzten Monaten akribisch verfolgt und nicht den Eindruck gewonnen, daß das bloße Ansprechen von Mängeln das Projektteam zu gravierenden Design-Änderungen veranlassen wird. Aber große Design-Änderungen wären nötig, um die angesprochenen Mängel zu beheben oder einige vermisste Features einzubauen (siehe meine Übersicht aus dem letzten Artikel). Auch ist der Fahrplan für die Integration in Java 8 zeitlich recht ambitioniert. Schon der für Mitte 2012 angekündigte EDR2 kam verspätet erst im September. Wichtige Design-Fragen sind in Threeten noch umstritten. Und es gibt immer noch viele Detail-Baustellen im gesamten API, an denen bis zum JDK-Feature-Freeze Ende Januar 2013 gearbeitet werden muß. Dazu kommt der Aufwand für die TCK-Tests, die zur Integration in das JDK notwendig sind. Threeten steht also unter erheblichem Zeitdruck. Vor diesem Hintergrund erscheint es mir absolut unwahrscheinlich, daß bloße Debattenbeiträge wie dieser hier das Threeten-Team zum Umdenken bewegen und es wieder bei Null starten lassen.

Ich möchte daher den anderen viel mühseligeren Weg beschreiten, eine Alternative zu Threeten zu entwickeln. Damit das überhaupt Sinn ergibt, muß es eine High-End-Alternative von hoher Qualität werden, die alle von mir genannten Lücken des Threeten-Ansatz beseitigt. Das ist sehr ambitioniert und schwer zu realisieren. Threeten ist zugute zu halten, die Meßlatte für eine Alternative sehr hoch zu legen. Ich glaube aber nach intensiven Vorstudien an die Machbarkeit. Das größte Problem sehe ich für mich eher in meinen begrenzten personellen Ressourcen. Daher kann ich keinen festen Zeitpunkt für die Veröffentlichung einer eigenen Bibliothek versprechen. Ich wünsche mir aber, daß ich wenigstens noch vor der Einführung von Java 8 (im September 2013 geplant) ein alpha-Release anbieten kann. Man wird sehen. Einen Namen habe ich für mein Projekt auch schon: Time4J, also der englische Name meines Blogs. Veröffentlichen werde ich das Projekt als Freund von Open-Source und vor allem freier Software unter der GPLv3-Lizenz.

 

Die Kommentarfunktion ist geschlossen.