DateTimeToUnix in UTC?

后端 未结 2 1912
佛祖请我去吃肉
佛祖请我去吃肉 2021-01-03 02:25

I need UTC variants of the functions DateTimeToUnix and UnixToDateTime, so a Chinese customer is able to interact with the server in Germany. Both

相关标签:
2条回答
  • 2021-01-03 03:01

    You have already found DateTimeToUnix and UnixToDateTime. So that part of the conversion is taken care of.

    All you need to do now is convert between local and UTC time. You can do that using DateUtils.TTimeZone class. Specifically DateUtils.TTimeZone.ToUniversalTime and DateUtils.TTimeZone.ToLocalTime.

    These four functions give you all that you need.

    0 讨论(0)
  • 2021-01-03 03:15

    I think I have found some solutions for my question. All 3 solutions gave the same output, but I will try to find out which one is best and I will test it on several machines with different locales.

    Solution #1 using TzSpecificLocalTimeToSystemTime and SystemTimeToTzSpecificLocalTime works fine, but requires Windows XP and above:

    (Source: https://stackoverflow.com/a/15567777/3544341 , modified)

    // Statically binds Windows API functions instead of calling them dynamically.
    // Requires Windows XP for the compiled application to run.
    {.$DEFINE USE_NEW_WINDOWS_API}
    
    {$IFDEF USE_NEW_WINDOWS_API}
    function SystemTimeToTzSpecificLocalTime(lpTimeZoneInformation: PTimeZoneInformation; var lpUniversalTime,lpLocalTime: TSystemTime): BOOL; stdcall; external kernel32 name 'SystemTimeToTzSpecificLocalTime';
    {$ELSE}
    function SystemTimeToTzSpecificLocalTime(lpTimeZoneInformation: PTimeZoneInformation; var lpUniversalTime,lpLocalTime: TSystemTime): BOOL; stdcall;
    var
      h: HModule;
      f: function(lpTimeZoneInformation: PTimeZoneInformation; var lpUniversalTime,lpLocalTime: TSystemTime): BOOL; stdcall;
    begin
      h := LoadLibrary(kernel32);
      if h = 0 then RaiseLastOSError;
    
      @f := GetProcAddress(h, 'SystemTimeToTzSpecificLocalTime');
      if @f = nil then RaiseLastOSError;
    
      result := f(lpTimeZoneInformation, lpUniversalTime, lpLocalTime);
    end;
    {$ENDIF}
    
    {$IFDEF USE_NEW_WINDOWS_API}
    function TzSpecificLocalTimeToSystemTime(lpTimeZoneInformation: PTimeZoneInformation; var lpLocalTime, lpUniversalTime: TSystemTime): BOOL; stdcall; external kernel32 name 'TzSpecificLocalTimeToSystemTime';
    {$ELSE}
    function TzSpecificLocalTimeToSystemTime(lpTimeZoneInformation: PTimeZoneInformation; var lpLocalTime, lpUniversalTime: TSystemTime): BOOL; stdcall;
    var
      h: HModule;
      f: function(lpTimeZoneInformation: PTimeZoneInformation; var lpLocalTime, lpUniversalTime: TSystemTime): BOOL; stdcall;
    begin
      h := LoadLibrary(kernel32);
      if h = 0 then RaiseLastOSError;
    
      @f := GetProcAddress(h, 'TzSpecificLocalTimeToSystemTime');
      if @f = nil then RaiseLastOSError;
    
      result := f(lpTimeZoneInformation, lpLocalTime, lpUniversalTime);
    end;
    {$ENDIF}
    
    function UTCToLocalDateTime_WinXP(d: TDateTime): TDateTime;
    var
      TZI: TTimeZoneInformation;
      LocalTime, UniversalTime: TSystemTime;
    begin
      GetTimeZoneInformation(tzi);
      DateTimeToSystemTime(d,UniversalTime);
      SystemTimeToTzSpecificLocalTime(@tzi,UniversalTime,LocalTime);
      Result := SystemTimeToDateTime(LocalTime);
    end;
    
    function LocalDateTimeToUTC_WinXP(d: TDateTime): TDateTime;
    var
      TZI: TTimeZoneInformation;
      LocalTime, UniversalTime: TSystemTime;
    begin
      GetTimeZoneInformation(tzi);
      DateTimeToSystemTime(d,LocalTime);
      TzSpecificLocalTimeToSystemTime(@tzi,LocalTime,UniversalTime);
      Result := SystemTimeToDateTime(UniversalTime);
    end;
    

    Solution #2 as workaround for older operating systems does also work fine:

    (Source: http://www.delphipraxis.net/299286-post4.html )

    uses DateUtils;
    
    function GetDateTimeForBiasSystemTime(GivenDateTime: TSystemTime; GivenYear: integer): TDateTime;
    var
      Year, Month, Day: word;
      Hour, Minute, Second, MilliSecond: word;
    begin
      GivenDateTime.wYear := GivenYear;
      while not TryEncodeDayOfWeekInMonth(GivenDateTime.wYear, GivenDateTime.wMonth, GivenDateTime.wDay, GivenDateTime.wDayOfWeek, Result) do
        Dec(GivenDateTime.wDay);
    
      DecodeDateTime(Result, Year, Month, Day, Hour, Minute, Second, MilliSecond);
      Result := EncodeDateTime(Year, Month, Day, GivenDateTime.wHour, GivenDateTime.wMinute, GivenDateTime.wSecond, GivenDateTime.wMilliseconds);
    end;
    
    function GetBiasForDate(GivenDateTime: TDateTime): integer;
    var
      tzi: TIME_ZONE_INFORMATION;
    begin
      GetTimeZoneInformation(tzi);
      if (GivenDateTime < GetDateTimeForBiasSystemTime(tzi.StandardDate, YearOf(GivenDateTime))) and
         (GivenDateTime >= GetDateTimeForBiasSystemTime(tzi.DaylightDate, YearOf(GivenDateTime))) then
        Result := (tzi.Bias + tzi.DaylightBias) * -1
      else
        Result := (tzi.Bias + tzi.StandardBias) * -1;
    end;
    
    function UTCToLocalDateTime_OldWin(aUTC: TDateTime): TDateTime;
    begin
      Result := IncMinute(aUTC, GetBiasForDate(aUTC));
    end;
    
    function LocalDateTimeToUTC_OldWin(aLocal: TDateTime): TDateTime;
    begin
      Result := IncMinute(aLocal, GetBiasForDate(aLocal) * -1);
    end;
    

    Solution #3 using TTimeZone for users of newer versions of Delphi, does give the same results as the codes above:

    (Solution by David Heffernan, alas not possible in my current project, because I am bound to Delphi 6)

    uses DateUtils;
    
    {$IF Declared(TTimeZone)}
    function UTCToLocalDateTime_XE(aUTC: TDateTime): TDateTime;
    begin
      result := TTimeZone.Local.ToLocalTime(aUTC);
    end;
    
    function LocalDateTimeToUTC_XE(aLocal: TDateTime): TDateTime;
    begin
      result := TTimeZone.Local.ToUniversalTime(aLocal);
    end;
    {$IFEND}
    

    Now we can put all 3 solutions together! :-)

    function UTCToLocalDateTime(aUTC: TDateTime): TDateTime;
    begin
      {$IF Declared(UTCToLocalDateTime_XE)}
      result := UTCToLocalDateTime_XE(aUTC);
      {$ELSE}
        {$IFDEF USE_NEW_WINDOWS_API}
        result := UTCToLocalDateTime_WinXP(aUTC);
        {$ELSE}
        try
          result := UTCToLocalDateTime_WinXP(aUTC);
        except
          on E: EOSError do
          begin
            // Workaround for Windows versions older than Windows XP
            result := UTCToLocalDateTime_OldWin(aUTC);
          end
          else raise;
        end;
        {$ENDIF}
      {$IFEND}
    end;
    
    function LocalDateTimeToUTC(aLocal: TDateTime): TDateTime;
    begin
      {$IF Declared(LocalDateTimeToUTC_XE)}
      result := LocalDateTimeToUTC_XE(aLocal);
      {$ELSE}
        {$IFDEF USE_NEW_WINDOWS_API}
        result := LocalDateTimeToUTC_WinXP(aLocal);
        {$ELSE}
        try
          result := LocalDateTimeToUTC_WinXP(aLocal);
        except
          on E: EOSError do
          begin
            // Workaround for Windows versions older than Windows XP
            result := LocalDateTimeToUTC_OldWin(aLocal);
          end
          else raise;
        end;
        {$ENDIF}
      {$IFEND}
    end;
    

    An easy method to get the current UTC unix timestamp is

    function NowUTC: TDateTime;
    var
      st: TSystemTime;
    begin
      GetSystemTime(st);
      result := EncodeDateTime(st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
    end;
    
    function CurrentUnixUTCTimestamp: int64;
    begin
      result := DateTimeToUnix(NowUTC);
    end;
    
    0 讨论(0)
提交回复
热议问题