User Tools

Site Tools


technology:datumswerte

Datums und Zeitwerte

Datums und Zeitwerte werden intern als Gleitkommazahl1) gespeichert. Das Problem ist, dass solch ein Gleitkommawert einen Bezug haben muss und zudem in unterschiedlichen Zeitzonen was anderes bedeuten kann. Hinzu kommt, dass die unterschiedliche Bedeutung sich ändern kann wenn sich die Regeln ändern. Wenn ich z.B. einen Termin im April 2024 für 08:00 eingetragen habe, und sich die Sommerzeitregelung ändern sollte2), kann das zu Problemen führen3).

Berechnungen mit Datumswerten

Die Berechnungen von Datumswerten ist sehr einfach. Der Unterschied zwischen 2 Werten wird z.B. einfach ermittelt, indem die Werte von einander abgezogen werden. Ob es dazwischen eine Zeitumstellung gibt kann das System nicht wissen4). Folgendes führt somit zu einem Wert von 1, obwohl am 28.10.2012 die Uhren eine Stunden zurück gestellt werden:

(new DateTime(2012, 10, 29) - new DateTime(2012, 10, 28)).TotalDays

Wenn man die Zeitumstellung berücksichtigen möchte, muss man das explizit machen. Das funktioniert, indem man den Datums/Zeitwert erst in UTC wandelt. Beim Wandeln wird das System5) die Zeitumstellung berücksichtigen, und folgendes hat zum Ergebnis 1,04166666666667 und nicht 1:

(new DateTime(2012, 10, 29).ToUniversalTime() - new DateTime(2012, 10, 28).ToUniversalTime()).TotalDays

Zusammenfassend tut das System bei Berechnungen immer so, als würde es mit UTC rechnen, berücksichtigt also keine Zeitzonen und Zeitumstellungen von Sommer- auf Winterzeit und zurück. Das wäre auch fürchterlich umständlich. Mit einfachen Methoden kann man das aber explizit von System verlangen, sollte es nötig sein6). Das kann u.U. für den Entwickler ziemlich aufwendig werden, wie das Beispiel mit Serienterminen zeigt.

Serientermine

Wenn ich einen Serientermin definiere, z.B. jeden Montag um 14:00, dann ist es absolut notwendig anzugeben, in welcher Zeitzone ich den Termin berechnet haben möchte. 14:00 in der Deutschen Zeit ist dabei nicht immer gleichbedeutend mit 08:00 in der Zeit an der Ostküste der USA, weil die Zeitumstellung dort unterschiedlich sein kann. Als Anwender möchte ich den Termin immer entweder um 14:00 Deutscher Zeit oder um 08:00 Amerikanischer Zeit errechnet haben. 14:00 Deutscher Zeit könnte auch mal 09:00 Amerikanische Zeit sein, das ist mir aus Anwendersicht aber egal solange mich nur die Deutsche Zeit interessiert. Wenn ich aber 3 Monate in New York arbeite, möchte ich meine Serientermine vielleicht in NY Zeit angeben, weil ich da aus 08:00 nicht plötzlich 09:00 errechnet haben möchte, weil es in Deutschland eine Zeitumstellung gab.

Das Fazit ist, dass man bei der Anlage für Serientermine immer speichern muss, in welcher Zeitzone die Vorkommen des Termins errechnet werden sollen. Man könnte sich auf die Einstellung der Zeitzone am Server verlassen7), nur wäre das Problem, dass man dann Werte nicht konsistent am Client berechnen lassen kann8) und vor allem, dass sich die Berechnung ändern würde, wenn man die Einstellung am Server ändert - und das darf absolut nicht passieren. Angezeigt werden können sie in jeder beliebigen Zeitzone, wichtig ist nur, dass sie konsistent und für den Anwender verständlich berechnet werden, und zwar mit der Zeitzone, mit der es möchte.

Umgang mit Zeitwerten in Businessanwendungen

In den Anwendungen der TAV Enterprise Software GmbH geht es um Businessanwendungen und nicht um Wissenschaftliche Anwendungen. Einige raten, die Uhrzeit als UTC zu speichern und immer hin-und-her zu konvertieren. Der Anwender gibt 08:00 an, das System konvertiert vor dem Speichern zu 06:00 in UTC. Beim erneuten laden der Daten zur Anzeige wird von 06:00 UTC zurück in die lokale Uhrzeit von 08:00 konvertiert.

Das wäre für einfache Datumsangaben eine vernünftige Lösung, weil die Konvertierung eindeutig ist - bis die Regierung sich dazu entschließt, die Sommerzeit abzuschaffen. In dem Fall würde alle UTC Datums/Uhrzeitwerte, die hinter der Umstellung liegen, anders zurück konvertiert werden nachdem das nächste Windows Update die neuen Regeln berücksichtigt9). Es gib also 3 Möglichkeiten:

  1. Eine Mandantenzeitzone verwenden und alle Datums-/Zeitwerte in der lokalen Zeit des Mandanten speichern,
  2. Alle Datums-/Zeitwerte in UTC speichern und abhängig des Clients zur Anzeige wandeln, dabei an einigen Stellen10) explizit speichern, welche Zeitzone zur Berechnung verwendet werden soll,
  3. Jeden einzelnen Datums-/Zeitwert in der Uhrzeit der Zeitzone speichern, in der er angegeben ist, gemeinsam mit der Zeitzone.

Jede Entscheidung hat Vor- und Nachteile.

Mandantenzeitzone

Das wurde bisher so gemacht, siehe softelligence P.A.C., bzw. die Delphi 6 Version von PRO•M. Die Vor- und Nachteile wurden oben bereits besprochen. Solange man immer nur von einer Zeitzone ausgeht und vom Anwender genügend Mitdenken voraussetzt, dass er die Tage der Zeitumstellung korrekt angibt, bzw. selbst anpasst, ist das die einfachste Lösung. Sobald das Unternehmen aber mal Anwender in Deutschland hat, mal in England, mal in den USA, mal woanders, klappt das nicht mehr. Mitarbeiter in den USA möchten ihre Daten in USA Zeit angeben, und wenn Termine koordiniert werden müssen, muss korrekt von USA in Deutsche Zeit und zurück gerechnet werden.

Man könnte das darüber regeln, dass es pro Zeitzone einen eigenen Mandanten gibt. Das wäre eine Lösung wenn man sich anfangs keine Gedanken gemacht hat, z.B. für die Delphi 6 Version von PRO•M. Es geht aber flexibler.

Alle Werte als UTC speichern

Die Lösung ist schon sehr gut. Sie hat den Nachteil, dass man die Werte in der Datenbank in UTC Zeit sieht, was in den meisten Fällen nicht der Zeit entspricht, die man selbst angegeben hat. Der andere Nachteil ist der, dass Änderungen an der Sommerzeitregelung die Konvertierung zurück in die ursprüngliche Zeit verändern könnte.

Uhrzeit der Zeitzone speichern, die für die Angabe verwendet wurde, samt Zeitzone selbst

Wenn ich einen Termin am 4. April 2030 um 08:00 in NY angebe, soll das immer so bleiben. Was das in UTC oder Deutscher Zeit ist, ist mir egal. Wenn ich das somit genauso abspeicher, habe ich genau das, was ich möchte. Wenn ich in der Denormalisierung zudem speicher, was der Offset ist (z.B. -5 Stunden für NY), bzw. die UTC Zeit selbst11), kann man die gespeicherten Werte vergleichbar halten und auch Abfragen bauen, die auf die UTC gehen12).

Lösung

  • Der Mandant hat eine Standard Zeitzone,
  • Der Mitarbeiter übernimmt die Zeitzone vom Mandanten, sie kann aber jederzeit überschrieben werden,
  • Datums- und Zeitangaben beinhalten immer die Zeitangaben in der Zeitzone des Mitarbeiters, gemeinsam mit der Zeitzonen Information selbst, damit man hin-und-her konvertieren kann,
  • Die denormalisierten Daten für Abfragen beinhalten zudem Datum und Uhrzeit im UTC Format um Vergleichbarkeit von den Werten herstellen zu können und konsistente Abfragen über Datumswerte zu ermöglichen.

Was ist mit Terminen, die eine Zeitumstellung in einer Zeitzone beinhalten? Ein Termin in Outlook, der in der Deutschen Zeitzone am 28. Oktober von 01:00-04:00 angegeben ist, wird mit einer Dauer von 3 Stunden angezeigt13). Wenn ich mir den Kalender mit US Amerikanischer Ostküstenzeit angucke, ist der Termin von 19:00-23:00 am Vorabend, und auch 4 Stunden und nicht mehr nur 3. Es gibt also auch hierfür einige Regeln, die sich an der Konvertierung von DateTime Werten in .Net anlehnt, siehe Zeitkonvertierung in .Net:

  • Bei von-bis Terminen wird der Start und das Ende angegeben und berücksichtigt.
  • Liegt einer der Daten in der sogenannten Todeszone14), wird der Datensatz abgelehnt. Wenn es aber durch Änderungen an den Regeln passieren sollte, dass eine alte Angabe in der Todeszone liegt, wird einfach der Standard-Offset abgezogen um die UTC-Zeit zu ermitteln. Beim hin-und-her Wandeln kommt dann eine andere lokale Uhrzeit raus15),
  • Liegt einer der Daten an einem zweideutigen Zeitpunkt16), muss der Anwender17) angeben welcher der Beiden gemeint ist18),
  • Bei Dauerangaben ist das schwieriger. Wenn ich am 28. Oktober 2012 um 02:00 einen Termin mit einer Dauer von einer Stunde angebe, wäre die lokale Uhrzeit des Terminendes 03:00, wobei das 03:00 Sommerzeit wäre, nicht die 03:00 Standardzeit, wodurch der Termin eine Dauer von 2 Stunde bekäme. Die Anzeige wäre in beiden Fällen dieselbe, ob die Dauer mit einer oder zwei Stunden angegeben ist. D.h. dass für Berechnungszwecke die Dauer verwendet werden muss, und nicht das Ende Datum/Uhrzeit. Auch Abfragen für Zeiträume müssten das berücksichtigen, wobei man bei den von-Zeiten immer den früheren nehmen muss, bei bis-Zeiten immer den späteren, um alle UTC Datumsangaben zu erhalten19)
1) Double oder Float
2) das entscheidet die Regierung, nicht Bill Gates
3) wenn der Wert als UTC Zeit gespeichert wird, der beim Konvertieren in die lokale Uhrzeit die Sommerzeit berücksichtigt - und wenn Windows heute und morgen andere Regeln für diese Konvertierung hat, führt das zu unterschiedlichen Ergebnissen
4) es könnte die lokale Einstellung verwenden, dann würden Berechnungen aber abhängig der Systemeinstellung zu unterschiedlichen Ergebnissen führen
5) Windows
6) z.B. wenn ich Stunden fakturiere, und in dem erfassten Zeitraum eine Änderung von Sommmer- auf Winterzeit liegt, und ich die 1 Stunde Arbeitszeit nicht verlieren möchte
7) eine Art Mandantenzeitzone
8) dort ist u.U. eine andere Zeitzone eingestellt als am Server
9) das ist in der Praxis nicht weiter schlimm, es ändert sich ja niemals etwas aus der Vergangenheit, solche Änderungen sind sehr selten, und werden lange vorher angekündigt. Es bleibt also ausreichend Zeit den Anwendern einen Pfad zu geben, die Termine, die in der Zukunft liegen, anzupassen, bzw. wird Windows die geänderten Regeln sicherlich lange Zeit berücksichtigen, noch bevor Anwender anfangen Termine so lange in der Zukunft anzugeben.
10) z.B. Berechnung von Serienterminvorkommen
11) und man bei Änderungen an Sommerzeitregeln nur die Denormalisierung anpassen muss
12) Abfragen auf die Zeit selbst wären kompliziert, weil 08:00 alles von 20:00 am Vorabend bis 20:00 heute bedeuten kann, abhängig der Zeitzone
13) obwohl es 4 Stunden sind, weil die Uhr um 3 Uhr zurück gestellt wird
14) 2:30 am letzten Sonntag im März gibt es nicht
15) z.B. wird aus 2:30 am letzten März in 2012 1:30 in UTC Zeit, weil wir in der +1 Offset Zeitzone sind, was beim zurück Wandeln in die lokale Zeit 3:30 ist, weil dann Sommerzeit ist mit +2
16) 2:30 am letzten Sonntag im November gibt es zwei mal
17) in einer späteren Version
18) ich gehe fest davon aus, dass es immer nur 2 Möglichkeiten gibt. Der Default wird immer Standardzeit sein, und der Anwender wird vorerst nicht die Möglichkeit haben, das zu ändern
19) so in der Art, das wird beim Programmieren klar, ist doch nicht so schwer!
technology/datumswerte.txt · Last modified: 2012/10/21 14:11 by rtavassoli