【问题标题】:What am I doing wrong converting these times from UTC to Local?将这些时间从 UTC 转换为本地时间我做错了什么?
【发布时间】:2019-07-28 18:04:14
【问题描述】:

使用 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 中的两种方法。特别是函数LocalDateTimeFromUTCDateTimeUnivDateTime2LocalDateTime 但是,结果都是倒退的。两种方法都不是增加 4 小时,而是减去 4 小时。

所以我从两者得到的结果是

2019-07-27 07:33:02

我知道我没有在早上 7:33 做那个推送。我还没醒。

事实上,如果我使用了错误函数DateTime2UnivDateTime()那么我实际上得到了正确的结果。

我在这里做错了什么,如何获得当地时间的正确结果?

我几乎不了解时区背后的科学。


编辑

看起来第一个函数导致时移加倍,所以我没有意识到它已经在尝试转换为本地时间。但不是减去 4 小时,而是减去了 8 小时。所以我被甩了,我想我仍然需要将它转换为当地时间。但为什么它会移动两次?

【问题讨论】:

  • System.DateUtils 中有一个 ISO8601ToDate 方法

标签: datetime delphi github-api utc


【解决方案1】:

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;

【讨论】:

  • 第一部分做到了。我使用的方法显然有缺陷。
猜你喜欢
  • 1970-01-01
  • 2019-03-04
  • 2018-07-27
  • 2012-02-29
  • 2014-07-18
  • 2021-02-04
  • 2010-09-15
  • 2011-05-01
相关资源
最近更新 更多