代码之家  ›  专栏  ›  技术社区  ›  Jerry Dodge

将这些时间从UTC转换为本地时,我做错了什么?

  •  0
  • Jerry Dodge  · 技术社区  · 6 年前

    使用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”字段(最后一次pushed)上的github api。使用上面的函数转换后,我得到(格式化为字符串):

    2019-07-27 11:33:02

    现在我把这个值转换成当地时间。我的本地时间是EST,我知道昨天下午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 回复  |  直到 6 年前
        1
  •  3
  •   zed    6 年前

    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;