Firebird 4.0 und DateTimeOffset und TIMESTAMP WITH TIME ZONE

Forum für neue Firebird-Anwender.

Moderator: thorben.braun

HalloGalli
Beiträge: 4
Registriert: Mi 10. Apr 2019, 14:36

Mi 10. Apr 2019, 14:48

Hallo!

Ich verwende Firebird 4.0 zusammen mit C# und .NET Connector.
Firebird 4.0 unterstützt bereits "TIMESTAMP WITH TIME ZONE".
http://www.ibexpert.net/IBE/index.php?n ... oneSupport

Ich weiß allerdings nicht, wie ich die korrekt in C# lade und schreibe. C# stellt dafür den Datentyp DateTimeOffset zur Verfügung, den ich in meinem Programm verwende.
Ich finde aber keinen passenden Typ oder Methode in dem FbReader.
Wie funktioniert das? Brauche ich einen Entwicklungsstand?

Vielen Dank schon im voraus.
Benutzeravatar
martin.koeditz
Beiträge: 87
Registriert: Sa 31. Mär 2018, 14:35

Mi 10. Apr 2019, 17:20

Hallo,

das ist in der Tat eine gute Frage. Vielleicht kann ein erfahrener .NET-Programmierer etwas hierzu beisteuern.

Gruß
Martin
Martin Köditz
it & synergy GmbH
bfuerchau
Beiträge: 70
Registriert: Mo 7. Mai 2018, 18:09

Do 11. Apr 2019, 11:40

Da habe die das aber ganz schön kompliziert gemacht.
Eine Integration der UTC-Zeit (also grundsätzlich ohne Zietzone) wäre da erheblich einfacher gewesen.
Aber sei's drum.

Um es einfach zu machen würde ich nun für die Anwendung mittels
Set timezone '+00:00'
Somit ist für diese Verbindung die Zeitzone auf UTC-Time gesetzt.
Dies kann man wohl ebenso auch in der Config.conf bereits einstellen.

Für die Anwendung muss ich dann bei Datum/Zeitfeldern die UTC-Zeit an Firebird übergeben, dies ist eine Eigenschaft des DateTime-Feldes, also MyDateTime.ToUniversalTime().

Wenn ein Timestamp aus dem Reader gelesen wird, so ist dieser automtisch mit der CurrentCulture deines Threads besetzt. Da du allerdings die UTC-Zeit bekommst, musst du diese in deine lokale Zeit umrechnen.
Dazu bemühst du halt einen bestimmten Konstruktor:

new DateTime(2019, 4, 11, 8 , 59, 0, DateTimeKind.Utc).ToLocalTime()
Die Datum/Zeitwerte entnimmst du aus dem gelesenen Timestamp.

Auf Grund der Zeitzone der Sitzung arbeitest du nun grundsätzlich mit UTC und bist dann nur an deine eigene Zeitzone gebunden.
Dies löst auch das Probem, dass der FB-Server in einer anderen Zeitzone laufen kann als die Anwendung.

Wenn du den Aufwand so nicht treiben willst, setze je Verbindung die Zeitzone, die du so z.B. herausbekommst:

$"+{(int)Math.Round((DateTime.Now - DateTime.Now.ToUniversalTime()).TotalHours, 0):D2}:00"

Localtime ist somit nun deine eigene Zeitzonenzeit, current_timestamp immer die Serverzeit, dessen Zeitzone du leider immer noch irgendwie abfragen musst.

Um allerdings korrekt (auch über die Zeitwechsel hinaus) Zeiten korrekt zu verarbeiten, sollte man generell nur UTC-Zeiten in einer Datenbank ablegen.

Der Datentyp "without timezone" speichert generell die UTC-Zeit in dem die Sitzungszeitzone abgezogen wird und beim auslesen dazugerechnet wird.
Daher kann bei Sommer/Winterzeitumstellung der Wert sich verändern, da ja die aktuelle Zeitzone verwendet wird.
Wird der Typ "with timezone" verwendet, wird die Zeitzone zum Zeitpunkt der Speicherung mit gespeichert und beim Auslesen wieder dazu gerechnet.
Nun habe ich dann aber das Problem der Vergleichbarkeit (siehe Doku), da ein Current_Timestamp bzw. Localtime die aktuelle Zeitzone berücksichtig.

Allerdings bedeutet Current_Timestamp immer noch Server-Zeitzone und sollte halt mit "at ..." umgerechnet werden, was Localtime ja macht!

Für Berechnungen von Zeitdauern muss man eigenltich immer in UTC umrechnen bzw. den UTC-Wert aus der Datenbank bekommen.
Beim Typ "with time zone" muss ich dazu die Extract-Funktion "extract(timezone_hour from mydatetime)" verwenden um wieder in UTC-Zeit oder per "mydatetime at time zone '+00'" in UTC umrechnen.

Ob so Tools wie Squirrel, IBExpert, SQL-Verbindungsserver o.ä. dies dann berücksichtigen können muss man mal sehen.
HalloGalli
Beiträge: 4
Registriert: Mi 10. Apr 2019, 14:36

Mo 15. Apr 2019, 18:16

Vielen Dank für die Informationen derweil. Allerdings ist das nicht ganz die Lösung, soweit ich das Verstanden habe.

Ich bekomme beispielsweise eine Buchung mit einer Lokalzeit aus Russland. Dieser Zeitversatz muss erhalten bleiben, der Server steht hier in Deutschland. Der Datentyp DateTimeOffset ist defakto UTC-Zeit + die Abweichung zur Zeitzone. Um das in der Datenbank zu speichern, gibt es den Datentyp TIMESTAMP WITH TIME ZONE. Ich erhalte also UTC-Zeit zusammen mit der Zeitzone für Moskau. Wenn ich hier in Deutschland eine Buchung vornehme, habe ich eine andere Zeitzone, aber auch dieser Versatz muss gespeichert werden. Für Berechtigungsanfragen ist immer die Lokalzeit verantwortlich, für Zeitdauern sind die UTC-Zeiten wichtig.

Ich arbeite also nicht mit CurrentTimeStamp innerhalb des Datenbank-Servers, sondern von außen wird mir aus einer potentiell unbekannten Zeitzone eine Info eingegeben. Diese muss ich so speichern, dass ich damit rechnen kann (d.h. UTC verfügbar) und für den buchenden wieder richtig in seiner Lokalzeit darstellen kann, selbst dann, wenn sich der darzustellende Rechner in einer anderen Zeitzone befindet!

Das ist also sehr wichtig, nicht abhängig von der Zeitzone des Rechners zu sein, auf dem man sich befindet.

Leider ist mir jetzt immer noch nicht klar, wie ich von C# in die Datenbank komme und umgekehrt.
bfuerchau
Beiträge: 70
Registriert: Mo 7. Mai 2018, 18:09

Di 16. Apr 2019, 14:02

Das kann ich schon verstehen, aber wie werden die Daten dann sortiert?
Auch in der Anzeige von Informationen, z.B. Fortschreibungsdaten, ist jeweils nur die lokale Zeitzone relevant.
Deshalb ist es zwingend erforderlich, Timestamps in UTC zu speichern, da auch nur damit Sommer/Winterzeit-Probleme gelöst sind.
Das Windows-/LinuxFilesystem speichert die Zeiten auch in UTC. Nur so ist garantiert, dass die Zeiten in jeder Zeitzone korrekt, bezogen auf mich, dargestellt und sortierbar ist.
Das Problem stellte sich für dich u.U. bisher nicht, da ein Current Timestamp ja immer die Serverzeit ist und Entstehungszeitpunkte die Reihenfolge beibehalten. Kleine Außnahme: Während der Sommerzeit-Umstellung kann es hier doppelte Zeiten geben, wenn man nicht von sich aus für UTC-Zeiten gesorgt hat.

Wenn die Zeitzone für dich wichtig ist, dann kannst du diese dem User ja zusätzlich anzeigen.
Aber was bringt dir das wenn auf der Ausgabe
10:00 +02 oder 11:00 -06
erscheint? Wichtig ist doch, dass 11:00 -06 auf jeden Fall vor 10:00 +02 liegt.

Deshalb finde ich die Firebirdlösung eher verwirrend als zielführend wenn beim Auslesen der Werte dann immer die ursprüngliche lokale Zeit angezeigt wird und somit eine Reihenfolge der Daten nicht mehr gewährleistet ist.
bfuerchau
Beiträge: 70
Registriert: Mo 7. Mai 2018, 18:09

Mi 17. Apr 2019, 09:27

Mir ist dazu noch eine Ergänzung eingefallen:
Stelle dir vor, du schreibst eine Webanwendung mit Hilfe eines Frameworks, dass dir typesave Tablesets, Klassen und Objekte bereitstellt.
Diesem Framework übergibst du nun eine Connection.
Alle Zugriffe erfolgen über die Klassen und Objekte.
Das Framework baut selbstständig die Verbindung auf, macht die SQL-Aktion und stellt die Verbindung dann in den Connectionpool, aus dem sie nach einem Timeout wieder endgültig verschwindet.
An welcher Stelle willst du nun den "Set Timzone" einbauen?
Wer garantiert dir, dass die nachfolgenden SQL's alle von der selben Connection bedient werden?

Wenn eine Datenbank nicht mit Zeitzonen umgehen kann und man darauf angewiesen ist, setzt man die Datenbankserver immer in die Zeitzone +00:00 damit man generell UTC-Zeit bekommt.
Den Rest ereldigt meist das Framework selber, oder glaubst du, dass ein Webclient seine Zeitzone an den Server übermittelt?
Benutzeravatar
martin.koeditz
Beiträge: 87
Registriert: Sa 31. Mär 2018, 14:35

Mi 17. Apr 2019, 12:03

Wenn eine Datenbank nicht mit Zeitzonen umgehen kann und man darauf angewiesen ist, setzt man die Datenbankserver immer in die Zeitzone +00:00 damit man generell UTC-Zeit bekommt.
Da hat er recht. In diesem Fall wird immer eine Referenzangabe benötigt, an der sich alle anderen orientieren. Quasi das federführende System. Und die Zeitzone serverseitig auf UTC zu stellen, ist dann die charmanteste Variante.

Gruß
Martin
Martin Köditz
it & synergy GmbH
HalloGalli
Beiträge: 4
Registriert: Mi 10. Apr 2019, 14:36

Mi 17. Apr 2019, 17:02

Danke, aber Fazit ist für mich: Es gibt bislang keine Unterstützung.

Die Anforderung, diese Daten in einen speziellen Datentyp speichern zu wollen, ist auch nichts ungewöhnliches. Andere Datenbanken (SQl Server, Sybase, Postgres, Oracle, ...) unterstützen das bereits und es gehört auch meines Wissens zum neuen SQL Standard.

Ich habe ein FeatureRequest aufgemacht.
http://tracker.firebirdsql.org/browse/DNET-878
bfuerchau
Beiträge: 70
Registriert: Mo 7. Mai 2018, 18:09

Mi 17. Apr 2019, 18:56

Genau, ein Timestamp ist nichts weiter als eine Zeitmarke die nichts darüber aussagt, in welcher Zeitzone sie erstellt wurde.
Deshalb verwendet man auch bei chronologischen Speichrungen grundsätzlich UnviversalTimeClock, UTC. Möchte man die Zeitzone aus irgendwelchen Gründen speichern kann keine Datenbank dich daran hindern.

Ob DateTimeOffset tatsächlich etwas mit SQL-Standard zu tun hat wage ich hier zu bezweifeln. Ich finde diese Information ausschließlich bei Microsoft's SQL-Server und halt den Schwierigkeiten bei Konvertierungen vom Server.
Der Typ entspricht ja exact dem der Firebirdfraktion.

Wie bereits mehrfach gesagt:
Meine Empfehlung für chronologische Informationen ist die UTC in einem Timestampfield. Alles andere führt unweigerlich zu Komplikationen.

Ach ja:
Wie kommt der Typ denn in einem Datatable-Objekt dann an?
Als String => nicht bindbar an ein Datetime-Steuerelement.
Als DateiTime => in UTC oder in ursprünglicher LocalTime?
HalloGalli
Beiträge: 4
Registriert: Mi 10. Apr 2019, 14:36

Do 18. Apr 2019, 14:17

Firebird 4.0 unterstützt bereits "TIMESTAMP WITH TIME ZONE".
http://www.ibexpert.net/IBE/index.php?n ... oneSupport
Das ist SQL Standard meines Wissens, daher auch die breite Unterstützung.
Fast alle Datenbanken unterstützen den Typen, das täten sie nicht, wenn es keine Anforderungen dafür gäbe.

Der Trick am "TIMESTAMP WITH TIME ZONE" liegt darin, dass es ein Zeitstempel mit Zeitzone ist.
http://www.ibexpert.net/IBE/index.php?n ... oneSupport

Wie schon mehrfach geschrieben, DateTimeOffset ist der zugehörige Typ in .NET, was bekanntlich nichts mit SQL zu tun hat.
Antworten