TDateTime zu POSIX



  • Ich will auch mal was zu Zeitproblemen schreiben 😉

    In der VCL gibt's den Datentyp TDateTime, der intern das Datum als OLE Automation Timestamp hält (Tage ab 31.12.1899 als Fließkommazahl). Dazu gibt es die Funktion DateTimeToUnix, mit der dieser Zeitstempel in einen POSIX Zeitstempel konvertiert werden kann. Soweit ich weiß sind die POSIX Zeitstempel immer UTC und werden über die Funktionen localtime und gmtime in die Datumskomponenten aufgeschlüsselt. Das Problem, was ich jetzt habe, ist dass die DateTimeToUnix Funktion mir nicht den UTC Zeitstempel zurückgibt, sondern auf den entsprechenden UTC Zeitstempel noch den Bias der aktuellen Zeitzone zum Zeitpunkt des Datums addiert.
    Beispiel:
    Der 15.07.2024 12:00:00 CEST DST (GMT +2:00) wird zu dem POSIX Zeitstempel 1721044800 umgerechnet. Wenn ich diesen Zeitstempel mit Epoch & Unix Timestamp Conversion Tools wieder in lesbare Form bringe kommt dabei 15.07.2024 12:00:00 UTC und 15.07.2024 14:00:00 GMT +2:00 DST raus. Wenn ich über das Tool das Datum umrechne kommt dabei 1721037600 raus, das sind genau 7.200 Sekunden weniger, was genau dem Bias von UTC zu CEST DST entspricht.
    Ist das ein Bug in der VCL, hat jemand Erfahrung damit? Oder ist meine Annahme falsch, dass ein POSIX Zeitstempel immer UTC ist?



  • Nach meiner kurzen suche wird POSIX Timestamp als in UTC angegeben definiert.
    Daher scheint es ein Bug in VLC zu sein.
    Eventuell konvertiert TDateTime::DateTimeToUnix den internen wert einfach ohne darauf zu achten ob der interne Wert in UTC ist oder nicht.
    Falls TDateTime conversion nach/von UTC anbietet, würde das das problem wohl "fixen", indem man erst TDateTime in UTC konvertiert und dann zu einem POSIX Timestamp



  • Ah, danke, das hat mich schon mal auf den richtigen Weg gebracht. Man hat der Funktion DateTimeToUnix einen default-Parameter verpasst, der angibt, ob der Zeitstempel UTC oder local ist. Mit

    std::time_t const posix_time = DateTimeToUnix( TDateTime( 2024, 7, 15, 12, 0, 0, 0 ), false );
    

    bekommt man den richtigen POSIX Zeitstempel. Allerdings fliegt mir mein Unittest beim 31.3.2024 2:00:00 mit einem Integerüberlauf in TTimeZone.GetUtcOffsetInSeconds um die Ohren. Bei Embarcadero machen die wohl keine Unittests o.O



  • LOL? das Datum hat in unix timestamp den wert 1.721.008.800 das ist noch weit weg von einem 32bit signed integer.

    Da könnte es eventuell einfacher sein denn offset nachträglich abzuziehen, wenn man die aktuelle Zeitzone kennt.

    Wobei das auch fehleranfällig ist, wenn man so bedenkt das in den letzten Jahren doch einige Zeitzonen sich verändert haben. Kann man relativ gut sehen wenn man die updates des tzdata packages bei debian sich anschaut.

    Hmm alternativ könnte man den wert aus TDateTime in einen iso8601 string konvertieren (via System.DateUtils.DateToISO8601) und diesen dann mit einer anderen library in einen unix timestamp konvertieren.
    z.b. mit std::chrono https://en.cppreference.com/w/cpp/chrono/parse
    oder chrono::date
    https://stackoverflow.com/questions/14504870/convert-stdchronotime-point-to-unix-timestamp
    https://howardhinnant.github.io/date/date.html



  • Das Kind ist schon in den Brunnen gefallen, ich schreibe in den Dateien den Zeitstempel weg, den ich durch die Konvertierung per DateTimeToUnix bekomme. Solange die Dateien in der gleichen Zeitzone gelesen werden funktioniert alles, nur in anderen Zeitzonen passen die Zeitstempel dann nicht mehr. Ich überlege gerade, wie man das nachträglich beheben könnte, aber da geht leider nix, weil die Zeitzone zum Zeitpunkt des Schreibens beim Lesen nicht mehr bekannt ist.



  • @DocShoe sagte in TDateTime zu POSIX:

    Das Kind ist schon in den Brunnen gefallen, ich schreibe in den Dateien den Zeitstempel weg, den ich durch die Konvertierung per DateTimeToUnix bekomme. Solange die Dateien in der gleichen Zeitzone gelesen werden funktioniert alles, nur in anderen Zeitzonen passen die Zeitstempel dann nicht mehr. Ich überlege gerade, wie man das nachträglich beheben könnte, aber da geht leider nix, weil die Zeitzone zum Zeitpunkt des Schreibens beim Lesen nicht mehr bekannt ist.

    Auch nicht mit "Handarbeit"? Ist nicht mehr rekonstruierbar, welches System eine Datei geschrieben hat und wie das konfiguriert war? Vielleicht sogar irgendeine ID oder ein Hostname in der Datei über den das indirekt geht? Dann hast du echt ein Problem - ich befürchte, da bleibt echt nur den Fehler flott zu beheben und bisher geschriebene Zeitstempel abzuschreiben.



  • @Finnegan sagte in TDateTime zu POSIX:

    @DocShoe sagte in TDateTime zu POSIX:

    Das Kind ist schon in den Brunnen gefallen, ich schreibe in den Dateien den Zeitstempel weg, den ich durch die Konvertierung per DateTimeToUnix bekomme. Solange die Dateien in der gleichen Zeitzone gelesen werden funktioniert alles, nur in anderen Zeitzonen passen die Zeitstempel dann nicht mehr. Ich überlege gerade, wie man das nachträglich beheben könnte, aber da geht leider nix, weil die Zeitzone zum Zeitpunkt des Schreibens beim Lesen nicht mehr bekannt ist.

    Auch nicht mit "Handarbeit"? Ist nicht mehr rekonstruierbar, welches System eine Datei geschrieben hat und wie das konfiguriert war? Vielleicht sogar irgendeine ID oder ein Hostname in der Datei über den das indirekt geht? Dann hast du echt ein Problem - ich befürchte, da bleibt echt nur den Fehler flott zu beheben und bisher geschriebene Zeitstempel abzuschreiben.

    Wenn ich das per Handarbeit machen müsste wäre das ein Full-Time Job für den Rest meines Arbeitslebens...



  • @DocShoe

    die üblichen Verdächtigen. Division durch 0?

    Ist auch nicht immer gleich zu erkennen 😉

    MFG



  • @firefly
    Nur zur Vollständigkeit:
    Ich kann die TDateTime local time über die C-runtime in den korrekten UTC Zeitstempel konvertieren, wenn das in der gleichen Zeitzone passiert, in der das TDateTime Objekt erzeugt wurde:

    TDateTime dt = Now();
    
    std::tm tm = { 0 };
    tm.wYear = YearOf( dt );
    tm.wMonth = MonthOf( dt );
    tm.wDay = DayOf( dt );
    tm.wHour = HourOf( dt );
    tm.wMinute = MinuteOf( dt );
    tm.wSecond = SecondOf( dt );
    std::time_t const posix = std::mktime( &tm );
    

    Was mich an der ganzen Sache nervt sind zwei Dinge:

    1. DateTimeToUnix verhält sich anders als erwartet
    2. wenn man DateTimeToUnix so benutzt, dass es das tut, was man erwartet, dann funktionierts für manche gültige Zeitstempel nicht und wirft eine Exception.

Anmelden zum Antworten