将这些时间从 UTC 转换为本地时间我做错了什么?
What am I doing wrong converting these times from UTC to Local?
使用 GitHub API,时间戳以 UTC 格式返回。我正在使用以下内容将它们转换为 Delphi TDateTime
...
with TXSDateTime.Create do
try
XSToNative('2019-07-27T19:33:02Z');
Result:= AsDateTime;
finally
Free;
end;
我不记得我是在哪里找到那个函数的。
值 2019-07-27T19:33:02Z
直接来自特定存储库的 "pushed_at" 字段(最后推送)上的 GitHub API。使用上面的函数转换后,我得到(格式化为字符串):
2019-07-27 11:33:02
现在我取这个值并尝试将它转换为本地时间。我的当地时间是美国东部时间,我知道我昨天在 3:33 下午最后一次推送它的特定存储库这一事实。我直接在 GitHub 的网站上确认了这一点。
我已经使用了 the top two answers on this question 中的两种方法。特别是函数 LocalDateTimeFromUTCDateTime
和 UnivDateTime2LocalDateTime
但是,结果都是倒退的。这两种方法都不是增加 4 小时,而是减去 4 小时。
所以我从两者得到的结果是
2019-07-27 07:33:02
我知道我没有在上午 7:33 进行推送。我还没醒
事实上,如果我使用 错误的 函数 DateTime2UnivDateTime()
那么我实际上得到了正确的结果。
我在这里做错了什么,我如何获得当地时间的正确结果?
我几乎不了解时区背后的科学。
编辑
看起来第一个函数导致时移加倍,所以我没有意识到它已经在尝试转换为当地时间。但不是减去 4 小时,而是减去 8 小时。所以我被抛弃了,认为我仍然需要将它转换为当地时间。但为什么要移动两次?
1) 将 ISO 时间转换为 Delphi TDateTime:
function ISOToDateTime(const AISODateTime: string): TDateTime;
var
I: Integer;
VDate, VTime: TDateTime;
VFormatSettings: TFormatSettings;
begin
// ISO format: 2009-07-06T01:53:23Z
VFormatSettings.DateSeparator := '-';
VFormatSettings.ShortDateFormat := 'yyyy-mm-dd';
VFormatSettings.TimeSeparator := ':';
VFormatSettings.ShortTimeFormat := 'hh:nn:ss';
I := Pos('T', AISODateTime);
VDate := StrToDate(Copy(AISODateTime, 1, I - 1), VFormatSettings);
VTime := StrToTime(Copy(AISODateTime, I + 1, 8), VFormatSettings);
Result := Trunc(VDate) + Frac(VTime);
end;
2) 将 UTC 时间转换为本地时间:
function UniversalToLocalTime(const AUtcTime: TDateTime): TDateTime;
function _GetSystemTzOffset: Extended;
var
VTmpDate: TDateTime;
ST1, ST2: TSystemTime;
TZ: TTimeZoneInformation;
begin
GetTimeZoneInformation(TZ);
DateTimeToSystemTime(AUtcTime, ST1);
SystemTimeToTzSpecificLocalTime(@TZ, ST1, ST2);
VTmpDate := SystemTimeToDateTime(ST2);
Result := MinutesBetween(VTmpDate, AUtcTime) / 60;
if VTmpDate < AUtcTime then begin
Result := -Result;
end;
end;
var
VOffset: Extended;
begin
VOffset := _GetSystemTzOffset;
if VOffset = 0 then begin
Result := AUtcTime;
end else begin
Result := IncHour(AUtcTime, Trunc(VOffset));
Result := IncMinute(Result, Round(Frac(VOffset) * 60));
end;
end;
使用 GitHub API,时间戳以 UTC 格式返回。我正在使用以下内容将它们转换为 Delphi TDateTime
...
with TXSDateTime.Create do
try
XSToNative('2019-07-27T19:33:02Z');
Result:= AsDateTime;
finally
Free;
end;
我不记得我是在哪里找到那个函数的。
值 2019-07-27T19:33:02Z
直接来自特定存储库的 "pushed_at" 字段(最后推送)上的 GitHub API。使用上面的函数转换后,我得到(格式化为字符串):
2019-07-27 11:33:02
现在我取这个值并尝试将它转换为本地时间。我的当地时间是美国东部时间,我知道我昨天在 3:33 下午最后一次推送它的特定存储库这一事实。我直接在 GitHub 的网站上确认了这一点。
我已经使用了 the top two answers on this question 中的两种方法。特别是函数 LocalDateTimeFromUTCDateTime
和 UnivDateTime2LocalDateTime
但是,结果都是倒退的。这两种方法都不是增加 4 小时,而是减去 4 小时。
所以我从两者得到的结果是
2019-07-27 07:33:02
我知道我没有在上午 7:33 进行推送。我还没醒
事实上,如果我使用 错误的 函数 DateTime2UnivDateTime()
那么我实际上得到了正确的结果。
我在这里做错了什么,我如何获得当地时间的正确结果?
我几乎不了解时区背后的科学。
编辑
看起来第一个函数导致时移加倍,所以我没有意识到它已经在尝试转换为当地时间。但不是减去 4 小时,而是减去 8 小时。所以我被抛弃了,认为我仍然需要将它转换为当地时间。但为什么要移动两次?
1) 将 ISO 时间转换为 Delphi TDateTime:
function ISOToDateTime(const AISODateTime: string): TDateTime;
var
I: Integer;
VDate, VTime: TDateTime;
VFormatSettings: TFormatSettings;
begin
// ISO format: 2009-07-06T01:53:23Z
VFormatSettings.DateSeparator := '-';
VFormatSettings.ShortDateFormat := 'yyyy-mm-dd';
VFormatSettings.TimeSeparator := ':';
VFormatSettings.ShortTimeFormat := 'hh:nn:ss';
I := Pos('T', AISODateTime);
VDate := StrToDate(Copy(AISODateTime, 1, I - 1), VFormatSettings);
VTime := StrToTime(Copy(AISODateTime, I + 1, 8), VFormatSettings);
Result := Trunc(VDate) + Frac(VTime);
end;
2) 将 UTC 时间转换为本地时间:
function UniversalToLocalTime(const AUtcTime: TDateTime): TDateTime;
function _GetSystemTzOffset: Extended;
var
VTmpDate: TDateTime;
ST1, ST2: TSystemTime;
TZ: TTimeZoneInformation;
begin
GetTimeZoneInformation(TZ);
DateTimeToSystemTime(AUtcTime, ST1);
SystemTimeToTzSpecificLocalTime(@TZ, ST1, ST2);
VTmpDate := SystemTimeToDateTime(ST2);
Result := MinutesBetween(VTmpDate, AUtcTime) / 60;
if VTmpDate < AUtcTime then begin
Result := -Result;
end;
end;
var
VOffset: Extended;
begin
VOffset := _GetSystemTzOffset;
if VOffset = 0 then begin
Result := AUtcTime;
end else begin
Result := IncHour(AUtcTime, Trunc(VOffset));
Result := IncMinute(Result, Round(Frac(VOffset) * 60));
end;
end;