![]() |
Konvertierung UnixTimeStamp nacht TDateTime und zurück
Moinmoin,
im Rahmen eines Projektes brauch ich die Umrechnung von TDatetime-Werten nach UnixTimeStamp und zurück. Das ganze aber manuell ohne Dateutils. Da die Funktionen aus der Codelib ![]() Die Umrechnung eines TDatetime-Wertes zu einem Unixtimestamp funktioniert auch schon:
Delphi-Quellcode:
Leider will die Umkehrfunktion (also von Unixtimestamp nach TDatetime) nicht so wirklich funktionieren.
const
cSecPerDay = 86400; cMSecPerDay = 86400000; cDelphiEpoche = -2209161600; //30.12.1899 als UTS function DateTimeToUnixtimestamp(const value:TDateTime):Int64; var TimePart : extended; Zeit : int64; datum: int64; begin zeit := Trunc(Abs(Frac(value))*cSecPerDay); //Zeitanteil in Sekunden timepart := frac(Abs(Frac(value))*cMSecPerDay); //Millisekunden datum := trunc(int(value)*cSecPerDay); //Anzahl der Tage mit Bezug Delphi-Epoche in Sekunden result := cDelphiEpoche+(datum+zeit); if (timepart > 0.5) then //Korrektur bei 500+ Millisekunden inc(result); end;
Delphi-Quellcode:
Ein Unterschied von 1 sek (+/-) wär ok, da ein Unixtimestamp keine Millisekunden berücksichtigt. Leider bekomme ich aber Unterschieder von teilweise mehreren Tagen.
function UnixTimeStampToDateTime(const value:int64):TDateTime;
var datum : int64; base,rest : int64; zeit,timepart : Extended; base := cDelphiEpoche - value; datum := base DIV cSecPerDay; rest := base MOD cSecPerDay; timepart := cMSecPerDay/ABS(rest); zeit := cSecPerDay/Abs(rest); if (Frac(abs(Timepart))>0.5) then zeit := cSecPerDay/(base - (datum*cSecPerday))+1; result := datum+zeit; end; Kann mir da jemand auf die Sprünge helfen ? Anmerkung: Das ganze sollte sowohl mit positiven als auch negativen Werten klarkommen. |
AW: Konvertierung UnixTimeStamp nacht TDateTime und zurück
Die Zeitzone würde ich gesondert behandeln. D.h. ich würde den Datetimewert vor der Konvertierung in UTC umrechnen und dann in die Unixtimestamp umrechnen. Im umgekehrten Fall würde ich den berechneten Datetimewert wieder in lokale Zeit umrechnen, falls das erforderlich sein sollte. Hier mein Vorschlag für die Umrechnung von UTC-Datetimewerte in die Unixtimestamp und zurück (nur grob mit einigen Testdaten von
![]()
Delphi-Quellcode:
uses
System.SysUtils; (* { Days between TDateTime basis (12/31/1899) and Unix time_t basis (1/1/1970) } UnixDateDelta = 25569; *) function UnixTimeToDatetime(UnixTime: Int64): TDatetime; begin Result := UnixTime/SecsPerDay + UnixDateDelta; end; function DatetimeToUnixTime(Datetime: TDatetime): Int64; var t: TDatetime; begin t := Datetime - UnixDateDelta; if t>=0 then Result := trunc(t*SecsPerDay + 0.5) else Result := trunc(t*SecsPerDay - 0.5); end; |
AW: Konvertierung UnixTimeStamp nacht TDateTime und zurück
Warum nicht die Funktionen aus DateUtils.pas verwenden?
Delphi-Quellcode:
uses nichts;
const UnixDateDelta = 25569; SecsPerDay = 86400; function DateTimeToUnix(const AValue: TDateTime): Int64; begin Result := Round((AValue - UnixDateDelta) * SecsPerDay); end; function UnixToDateTime(const AValue: Int64): TDateTime; begin Result := AValue / SecsPerDay + UnixDateDelta; end; |
AW: Konvertierung UnixTimeStamp nacht TDateTime und zurück
@samso
Nah drann. Bei den (Test)-Daten hauts nicht hin: So, 23.08.1733 18:33:44 -60759,7734259259 -7458672376 Bei DateTimeToUnix fehlen 2 Sekunden. Bei UnixToDateTime mehr als ein Tag. :) @KodeZwerk Das ganze soll möglichst unabhängig von anderen (auch Delphi-Eigenen) Units laufen. btw leided deine Variante unter dem gleichen Problem wie Samso's |
AW: Konvertierung UnixTimeStamp nacht TDateTime und zurück
Zitat:
|
AW: Konvertierung UnixTimeStamp nacht TDateTime und zurück
@KodeZwerg
Da ich die Starter verwende hab ich keine .pas zu den Delphiegenen Units :) Lustig ist nur, das die von dir gepostet variante bei oben genannten Datum das gleiche fehlerhafte Ergebnis liefert. Die orginal Funktion dagegen haut hin. So wie es aussieht hackt es bei Daten vor dem 30.12.1899 00:00:00 (also der "Delphi-Epoche") |
AW: Konvertierung UnixTimeStamp nacht TDateTime und zurück
Wenn man sich die SysUtils.pas anschaut, vielleicht ist das von Bedeutung für Dein Problem.
Delphi-Quellcode:
{ Days between 1/1/0001 and 12/31/1899 }
DateDelta = 693594; { Days between TDateTime basis (12/31/1899) and Unix time_t basis (1/1/1970) } UnixDateDelta = 25569; |
AW: Konvertierung UnixTimeStamp nacht TDateTime und zurück
Zitat:
|
AW: Konvertierung UnixTimeStamp nacht TDateTime und zurück
Ja, das 1899-Problem habe ich nun auch erkannt. Das sollte schon mal besser sein:
Delphi-Quellcode:
Für DateTimeToUnix habe ich noch keine kompakte Lösung.
function DatetimeToUnixTime(Datetime: TDatetime): Int64;
var t: TDatetime; begin if Datetime<0 then t := trunc(Datetime) - frac(Datetime) - UnixDateDelta else t := Datetime - UnixDateDelta; if t>0 then Result := trunc(t*SecsPerDay + 0.5) else Result := trunc(t*SecsPerDay - 0.5); end; |
AW: Konvertierung UnixTimeStamp nacht TDateTime und zurück
Naja...DateTimeToUnix tut ja so:
Delphi-Quellcode:
auch bei Daten vor 31.12.1899.
var
Zeit : int64; datum: int64; begin zeit := Trunc((Abs(Frac(value))*cSecPerDay)+0.5); //Zeitanteil in Sekunden datum := trunc(int(value)*cSecPerDay); //Anzahl der Tage mit Bezug Delphi-Epoche in Sekunden result.fvalue := cDelphiEpocheUT+(datum+zeit); end; Nur die UnixTimeStamp nach TDateTime leidet hier unter dem 1899'er problem. Ich probier grad, ob ich mit der Info von KodeZwerk hinsichtlich DateDelta was rauskrieg. |
AW: Konvertierung UnixTimeStamp nacht TDateTime und zurück
Ich verstehe das rumfummeln nicht.
Du hast zwar nicht die Quelltexte in der Starter, kannst doch aber trotzdem auf die Funktionen von System.DateUtils zugreifen? Die Umwandlung davon sind doch richtig und korrekt? Dieser NonRTL-Ansatz ist doch Käse. Da müsstest du doch konsequenterweise auch auf System.Trunc verzichten.
Delphi-Quellcode:
program Project5;
{$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, System.DateUtils; procedure Main; var DateTimeStr: string; TimeStr: string; MyTime: TTime; MyDate: TDate; MyDateTime: TDateTime; UnixTimeStamp: Int64; begin DateTimeStr := '23.08.1733 18:33:44'; Writeln('Testdatum: ', DateTimeStr); MyDateTime := StrToDateTime(DateTimeStr); Writeln('Umwandung in TDateTime: ', Double(MyDateTime).ToString); UnixTimeStamp := System.DateUtils.DateTimeToUnix(MyDateTime); Writeln('UnixTimeStamp: ', UnixTimeStamp.ToString); MyDateTime := System.DateUtils.UnixToDateTime(UnixTimeStamp); DateTimeStr := FormatDateTime('DD.MM.YYYY HH:MM:SS', MyDateTime); Writeln('Rückwandlung von Timestamp in TDateTime und in String: ', DateTimeStr); Trunc end; begin try Main; Readln; except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; end. |
AW: Konvertierung UnixTimeStamp nacht TDateTime und zurück
@TiGU
ganz so einfach ist es nicht, wie schon in #4 vermeldet passen die Ergebnisse nicht.
Delphi-Quellcode:
also TiGU welcher Code hat Dein vertrauen?
program UT2;
{$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, System.DateUtils; const <- aus DateUtils Delphi 2009 _UnixDateDelta = 25569; _SecsPerDay = 86400; function _DateTimeToUnix(const AValue: TDateTime): Int64; begin Result := Round((AValue - _UnixDateDelta) * _SecsPerDay); end; function _UnixToDateTime(const AValue: Int64): TDateTime; begin Result := AValue / _SecsPerDay + _UnixDateDelta; end; procedure Main; var DateTimeStr: string; MyDateTime: TDateTime; UnixTimeA, UnixTimeB : Int64; begin DateTimeStr := '23.08.1733 18:33:44'; Writeln('Testdatum: ', DateTimeStr); MyDateTime := StrToDateTime(DateTimeStr); Writeln('Umwandung in TDateTime: ', Double(MyDateTime).ToString); UnixTimeA := System.DateUtils.DateTimeToUnix(MyDateTime); Writeln('UnixTime(DateUtils - XE8 ): ', UnixTimeA.ToString); UnixTimeB := _DateTimeToUnix(MyDateTime); Writeln('UnixTime(DateUtils - D2009): ', UnixTimeB.ToString); Writeln('Rückwandlung von UnixTime in TDateTime und in String'); Writeln('(DateUtils - XE8 ): ', FormatDateTime('DD.MM.YYYY HH:MM:SS', System.DateUtils.UnixToDateTime(UnixTimeA))); Writeln('(DateUtils - D2009): ', FormatDateTime('DD.MM.YYYY HH:MM:SS', _UnixToDateTime(UnixTimeA))); end; begin try Main; Readln; except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; end. |
AW: Konvertierung UnixTimeStamp nacht TDateTime und zurück
Zitat:
Das früher mal etwas nicht gestimmt hat kann ja gut sein, aber jetzt ist das doch nicht mehr relevant. |
AW: Konvertierung UnixTimeStamp nacht TDateTime und zurück
Mit der alten Version da habe ich Schuld, ich sah nur vom Ghostwalger den Source, startete mein D2009 und teilte den Inhalt. Tut mir leid Khostwalker und alle anderen!!
Ich habe es selber noch nicht getestet aber ich vermute für vor 1899 muss man das andere Delta nehmen (wenn Input negativ ist?). edit Ps: Die Konstanten stammen bei mir aus SysUtils.pas nicht aus DateUtils.pas. edit #2 Also die Tokyo Version bekomme ich nicht so einfach raus kopiert, die greift auf viele Sub-Funktionen zurück aber wie man am Alter Mann Beispiel sehen kann, funktioniert der "XE8-Way" (auch in Starter enthalten) weitaus zuverlässiger ohne weiteres zu tun oder herum rechnen und biegen. |
AW: Konvertierung UnixTimeStamp nacht TDateTime und zurück
@TiGu
Ob der verzicht nun sinnvoll ist oder nicht tut nix zur Frage und dem Problem :) Die Lösung, die KodeZwerg gepostet hat, ist auch die, die an den meisten Stellen im Internet rumgeistert (vgl. CodeLib, SwissDelphiCenter.ch u.v.m), aber genau das Problem mit Daten vor 1899 hat. Wie steht es den in den neueren Versionen drinn ? |
AW: Konvertierung UnixTimeStamp nacht TDateTime und zurück
mein Beitrag war falsch.
|
AW: Konvertierung UnixTimeStamp nacht TDateTime und zurück
So, ich melde mich wieder zurück, mit folgendem Vorschlag:
Delphi-Quellcode:
function UnixTimeToDatetime(UnixTime: Int64): TDatetime;
var t: Double; begin t := UnixTime/SecsPerDay + UnixDateDelta; if t>=0 then Result := t else Result := trunc(t) - frac(t) - 2; end; |
AW: Konvertierung UnixTimeStamp nacht TDateTime und zurück
Liste der Anhänge anzeigen (Anzahl: 1)
Wie man hier sehen kann, funktionieren 3 Methoden. Anhang 49213
Tokyo per Units, D2009 per Code, Samso per Code. Die GW Methode aus Post #1 bei Rückverwandlung klappt etwas nicht. Der Fehler mit D2009 Rückverwandlung entstand weil UnixTimeA verwendet wurde und nicht sein zugewiesener UnixTimeB. Nun A B C D vorhanden :-)
Delphi-Quellcode:
edit
program UT2;
{$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, System.DateUtils; const _DateDelta = 693594; _UnixDateDelta = 25569; _HoursPerDay = 24; _MinsPerHour = 60; _SecsPerMin = 60; _MSecsPerSec = 1000; _MinsPerDay = _HoursPerDay * _MinsPerHour; _SecsPerDay = _MinsPerDay * _SecsPerMin; _SecsPerHour = _SecsPerMin * _MinsPerHour; _MSecsPerDay = _SecsPerDay * _MSecsPerSec; _DelphiEpoche = -2209161600; //30.12.1899 als UTS function _DateTimeToUnix(const AValue: TDateTime): Int64; begin Result := Round((AValue - _UnixDateDelta) * _SecsPerDay); end; function _UnixToDateTime(const AValue: Int64): TDateTime; begin Result := AValue / _SecsPerDay + _UnixDateDelta; end; function _UnixTimeToDatetime(UnixTime: Int64): TDatetime; var t: Double; begin t := UnixTime / _SecsPerDay + _UnixDateDelta; if t > 0 then Result := t else Result := trunc(t) - frac(t) - 2; end; function _DateTimeToUnixTime(Datetime: TDatetime): Int64; var t: TDatetime; begin if Datetime < 0 then t := trunc(Datetime) - frac(Datetime) - _UnixDateDelta else t := Datetime - _UnixDateDelta; if t>0 then Result := trunc(t * _SecsPerDay + 0.5) else Result := trunc(t * _SecsPerDay - 0.5); end; function _DateTimeToUnixtimestamp(const value:TDateTime):Int64; var TimePart : extended; Zeit : int64; datum: int64; begin zeit := Trunc(Abs(Frac(value)) * _SecsPerDay); //Zeitanteil in Sekunden timepart := frac(Abs(Frac(value)) * _MSecsPerDay); //Millisekunden datum := trunc(int(value) * _SecsPerDay); //Anzahl der Tage mit Bezug Delphi-Epoche in Sekunden result := _DelphiEpoche + (datum+zeit); if (timepart > 0.5) then //Korrektur bei 500+ Millisekunden inc(result); end; function _UnixTimeStampToDateTime(const value:int64):TDateTime; var datum : int64; base,rest : int64; zeit,timepart : Extended; begin base := _DelphiEpoche - value; datum := base DIV _SecsPerDay; rest := base MOD _SecsPerDay; timepart := _MSecsPerDay/ABS(rest); zeit := _SecsPerDay/Abs(rest); if (Frac(abs(Timepart))>0.5) then zeit := _SecsPerDay/(base - (datum*_SecsPerday))+1; result := datum+zeit; end; procedure Main; var DateTimeStr: string; MyDateTime: TDateTime; UnixTimeA, UnixTimeB, UnixTimeC, UnixTimeD : Int64; begin DateTimeStr := '23.08.1733 18:33:44'; Writeln('Testdatum: ', DateTimeStr); MyDateTime := StrToDateTime(DateTimeStr); Writeln('Umwandung in TDateTime: ', Double(MyDateTime).ToString); UnixTimeA := System.DateUtils.DateTimeToUnix(MyDateTime); Writeln('DateTimeToUnix (Tokyo): ', UnixTimeA.ToString); UnixTimeB := _DateTimeToUnix(MyDateTime); Writeln('DateTimeToUnix (D2009): ', UnixTimeB.ToString); UnixTimeC := _DateTimeToUnixTime(MyDateTime); Writeln('DateTimeToUnix (Samso): ', UnixTimeC.ToString); UnixTimeD := _DateTimeToUnixtimestamp(MyDateTime); Writeln('DateTimeToUnix (GWalg): ', UnixTimeD.ToString); Writeln('Rückwandlung von UnixTime in TDateTime und in String'); Writeln('UnixToDateTime (Tokyo): ', FormatDateTime('DD.MM.YYYY HH:MM:SS', System.DateUtils.UnixToDateTime(UnixTimeA))); Writeln('UnixToDateTime (D2009): ', FormatDateTime('DD.MM.YYYY HH:MM:SS', _UnixToDateTime(UnixTimeB))); Writeln('UnixToDateTime (Samso): ', FormatDateTime('DD.MM.YYYY HH:MM:SS', _UnixTimeToDateTime(UnixTimeC))); Writeln('UnixToDateTime (GWalg): ', FormatDateTime('DD.MM.YYYY HH:MM:SS', _UnixTimeStampToDateTime(UnixTimeD))); end; begin try Main; Readln; except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; end. Fazit: Für volle Unit Unabhängigkeit und sehr hohe Kompatibilität Benutze Samsos Umsetzung. D2009 nur von und mit D2009 Code, dann klappt der auch ganz gut. |
AW: Konvertierung UnixTimeStamp nacht TDateTime und zurück
gelöscht - meine Korrektur für Deine Rückverwandlung war mehr als falsch. Sorry, ich versuchs nochmal.
|
AW: Konvertierung UnixTimeStamp nacht TDateTime und zurück
Vielen Dank für eure Mühen :)
Samso's Variante funktioniert perfekt :) |
AW: Konvertierung UnixTimeStamp nacht TDateTime und zurück
Deine Rückverwandlung konnte ich halbwegs Reparieren aber wenn ich Härtetest durchführe, je näher man dem Datum "31.12.1899 23:59:59" kommt umso ungenauer wird es mit dem "DelphiEpoche"-Stil, da spielen die Werte verrückt da Du den "30.12.1899" gewählt hast, die Zeit geht irgendwie auch immer verloren, da habe ich auch keine Lösung für gefunden.
|
AW: Konvertierung UnixTimeStamp nacht TDateTime und zurück
Für D2009 habe ich einen mini-Hotfix
Delphi-Quellcode:
Es ist allerdings anzumerken, _UnixToDateTime() hat noch einen Fehler!!
function _DateTimeToUnix(const AValue: TDateTime): Int64;
begin if AValue >= 0 then Result := Round((AValue - _UnixDateDelta) * _SecsPerDay) else Result := 172798 + Round((AValue - _UnixDateDelta) * _SecsPerDay); end; function _UnixToDateTime(const AValue: Int64): TDateTime; begin if AValue >= 0 then Result := AValue / _SecsPerDay + _UnixDateDelta else Result := (AValue - 172798) / _SecsPerDay + _UnixDateDelta; end; Die Sekunden-Angabe schwankt was zum Beispiel bei 23:59:59 auch den nächsten Tag zur Folge haben könnte !! Oder anders herum kann auch aus ein 00:00:00 der vorige Tag werden !! Je nachdem wohin die MSecs schwanken +0.5 oder -0.5. Ich hoffe es ist Verständlich was ich meine. |
AW: Konvertierung UnixTimeStamp nacht TDateTime und zurück
Zitat:
Delphi-Quellcode:
uses
SysUtils, Math; function UnixTimeToDatetime(UnixTime: Int64): TDatetime; var d, t: Double; begin d := UnixTime/SecsPerDay + UnixDateDelta; t := frac(d); if (d>=0) or IsZero(t) then Result := d else Result := trunc(d) - t - 2; end; |
AW: Konvertierung UnixTimeStamp nacht TDateTime und zurück
@Samso
Jup, aber nur bei Daten vor oder gleich dem 30.12.1899 . Habs nochmal korregiert, lt. meinen Unit-Tests funktionierts so:
Delphi-Quellcode:
function UnixToDateTime(const value:int64):TDateTime
var d, t: Extended; begin d := value/cSecPerDay + cUnixEpocheDT; t := frac(d); if (value <= cDelphiEpocheUT) then begin if (d>=0) or IsZero(t) then Result := d else Result := trunc(d) - t - 2; end else result := d; end; |
AW: Konvertierung UnixTimeStamp nacht TDateTime und zurück
Die Fälle in denen das Datum >= 30.12.1899 ist, wird bereits durch die Abfrage "d>=0" behandelt und führt immer in den Zweig "Result := d". Deshalb ist die Abfrage value <= cDelphiEpocheUT redundant.
|
AW: Konvertierung UnixTimeStamp nacht TDateTime und zurück
Mein D2009 Hotfix ist auch nicht das gelbe vom Ei.
Meine Problem Daten sind diese hier 29-31.12.1899 00:00:00/23:59:59 und hört hier auf 31.12.1969 23:59:59. Alles au0erhalb dieses Bereichs funktioniert mit der D2009 Methode auf Anhieb, alles was da zwischen liegt kann Probleme bereiten. edit Ich bastel noch ein wenig am D2009 rum, wenn ich mir aus den Werten einen TimeStamp erzeuge kann ich sehr einfach das lösen was D2009 noch falsch macht. Für alles zwischen 01.01.1900 00:00:00 und 01.01.1970 00:00:00 muss man einen Tag und 2 Sekunden dazu addieren. Probleme habe ich noch wenn man exakt 31.12.1899 bzw 31.12.1969 verwendet, aber das bekomme ich auch noch gefixt. Am ToUnix muss ich auch nochmal ran, der Interpretiert auch noch nicht alles korrekt was dann logischerweise zu einem falschen Ergebnis führt, also alles mehr oder minder leicht zu korrigieren wenn man genug Zeit investiert um alle möglichen Datums angaben durchzutesten :-) |
AW: Konvertierung UnixTimeStamp nacht TDateTime und zurück
@Samso
Jup..hast recht. Damit funktionieren jetzt beide Varianten (Unix->TDateTime und TDateTime->Unix). |
Alle Zeitangaben in WEZ +1. Es ist jetzt 07:36 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz