UTC中的DateTimeToUnix?

Dan*_*all 5 unix delphi time timestamp date

我需要的功能UTC变种DateTimeToUnixUnixToDateTime,所以中国客户能够与德国的服务器进行交互.双方应该能够交换Unix时间戳(UTC,没有DST)并能够通过这种方式进行通信.

HeidiSQL的一个bug报告中,用户讨论了这个DateTimeToUnix并且UnixToDateTime不关心时区,我在那里找到了以下代码:

function DateTimeToUTC(dt: TDateTime): Int64;
var
  tzi: TTimeZoneInformation;
begin
  Result := DateTimeToUnix(dt);
  GetTimeZoneInformation(tzi);
  Result := Result + tzi.Bias * 60;
end;
Run Code Online (Sandbox Code Playgroud)

MSDN 解释 twi.Bias如下:

UTC时间和当地时间之间的所有翻译均基于以下公式:

UTC =当地时间+偏见

偏差是UTC时间和当地时间之间的差异,以分钟为单位.

这听起来合乎逻辑,但由于我不确定上面的代码是否正确,我制作了以下程序来检查它:

// A date in summer time (DST)
Memo1.Lines.add('1401494400'); // 31 May 2014 00:00:00 GMT according to http://www.epochconverter.com/
Memo1.Lines.add(inttostr(DateTimeToUnixUTC(StrToDate('31.05.2014'))));

// A date in winter time
Memo1.Lines.add('567302400'); // 24 Dec 1987 00:00:00 GMT according to http://www.epochconverter.com/
Memo1.Lines.add(inttostr(DateTimeToUnixUTC(StrToDate('24.12.1987'))));
Run Code Online (Sandbox Code Playgroud)

目前德国(GMT + 1 + DST)的产量为:

1401494400
1401490800
567302400
567298800
Run Code Online (Sandbox Code Playgroud)

我预计输出是:

1401494400
1401494400
567302400
567302400
Run Code Online (Sandbox Code Playgroud)

我究竟做错了什么?

PS:对于这个项目,我受到Delphi 6的约束.

Dav*_*nan 5

你已经找到了DateTimeToUnixUnixToDateTime.因此,部分转换得到了解决.

您现在需要做的就是在本地和UTC时间之间进行转换.你可以使用DateUtils.TTimeZone类来做到这一点.特别是DateUtils.TTimeZone.ToUniversalTimeDateUtils.TTimeZone.ToLocalTime.

这四个功能为您提供所需的一切.

  • 您的解决方案http://stackoverflow.com/a/15567387/3544341不正确.当UTC unix时间是567302400时,我的时钟是1点,因为我们有冬天的时间.但是你的功能输出2点.但是,我找到了其他两个解决方案,它们显示了正确的时间(请参阅我自己的答案).这些解决方案是TzSpecificLocalTimeToSystemTime(需要WinXP +),或者适用于较旧Windows版本的Delphi解决方案,或TTimeZone,它也可以正常工作,但不适用于我的特殊情况,因为我为这个项目绑定了Delphi 6. (2认同)

Dan*_*all 5

我想我已经为我的问题找到了一些解决方案。所有这三种解决方案都提供了相同的输出,但是我将尝试找出哪一种是最佳的,并在具有不同语言环境的多台计算机上对其进行测试。

解决方案#1使用TzSpecificLocalTimeToSystemTimeSystemTimeToTzSpecificLocalTime工作正常,但需要Windows XP及更高版本:

(来源:https : //stackoverflow.com/a/15567777/3544341,修改)

// 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;
Run Code Online (Sandbox Code Playgroud)

解决方案2作为较旧操作系统的解决方法也可以正常工作:

(来源: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;
Run Code Online (Sandbox Code Playgroud)

TTimeZone为较新版本的Delphi的用户使用的解决方案#3的结果与上述代码相同:

David Heffernan提出的解决方案,在我当前的项目中是不可能的,因为我必须绑定到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}
Run Code Online (Sandbox Code Playgroud)

现在,我们可以将所有3个解决方案放在一起!:-)

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;
Run Code Online (Sandbox Code Playgroud)

获取当前UTC Unix时间戳的一种简单方法

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;
Run Code Online (Sandbox Code Playgroud)