【问题标题】:How do I convert milliseconds to a TDateTime?如何将毫秒转换为 TDateTime?
【发布时间】:2012-08-27 01:04:43
【问题描述】:

我正在做一个很长的循环下载数千个文件。我想显示估计的剩余时间,因为它可能需要几个小时。但是,根据我所写的,我得到了平均 毫秒。如何将此平均下载时间从毫秒转换为TDateTime

看看我在哪里设置Label1.Caption

procedure DoWork;
const
  AVG_BASE = 20; //recent files to record for average, could be tweaked
var
  Avg: TStringList; //for calculating average
  X, Y: Integer; //loop iterators
  TS, TE: DWORD; //tick counts
  A: Integer; //for calculating average
begin
  Avg:= TStringList.Create;
  try
    for X:= 0 to FilesToDownload.Count - 1 do begin //iterate through downloads
      if FStopDownload then Break; //for cancelling
      if Avg.Count >= AVG_BASE then //if list count is 20
        Avg.Delete(0); //remove the oldest average
      TS:= GetTickCount; //get time started
      try
        DownloadTheFile(X); //actual file download process
      finally
        TE:= GetTickCount - TS; //get time elapsed
      end;
      Avg.Add(IntToStr(TE)); //add download time to average list
      A:= 0; //reset average to 0
      for Y:= 0 to Avg.Count - 1 do //iterate through average list
        A:= A + StrToIntDef(Avg[Y], 0); //add to total download time
      A:= A div Avg.Count; //divide count to get average download time
      Label1.Caption:= IntToStr(A); //<-- How to convert to TDateTime?
    end;
  finally
    Avg.Free;
  end;
end;

PS - 我对计算最近 2​​0 次(或 AVG_BASE)下载的平均速度的不同方法持开放态度,因为我确信我的字符串列表解决方案不是最好的。我不想根据所有下载来计算它,因为速度可能会随着时间的推移而改变。因此,我只检查最后 20 个。

【问题讨论】:

    标签: delphi datetime-conversion


    【解决方案1】:

    TDateTime 值本质上是double,其中整数部分是天数,小数部分是时间。

    一天有 24*60*60 = 86400 秒(在 SysUtils 中声明的 SecsPerDay 常量)所以要获得 A 作为 TDateTime 做:

    dt := A/(SecsPerDay*1000.0); // A is the number of milliseconds 
    

    更好的计时方法是在单元Diagnostics 中使用TStopWatch 构造。

    例子:

    sw.Create;
    .. 
    sw.Start;
    // Do something
    sw.Stop;
    A := sw.ElapsedMilliSeconds;
    // or as RRUZ suggested ts := sw.Elapsed; to get the TimeSpan 
    

    要获得您的平均时间,请考虑使用此 moving average 记录:

    Type
      TMovingAverage = record
      private
        FData: array of integer;
        FSum: integer;
        FCurrentAverage: integer;
        FAddIx: integer;
        FAddedValues: integer;
      public
        constructor Create(length: integer);
        procedure Add( newValue: integer);
        function Average : Integer;
      end;
    
    procedure TMovingAverage.Add(newValue: integer);
    var i : integer;
    begin
      FSum := FSum + newValue - FData[FAddIx];
      FData[FAddIx] := newValue;
      FAddIx := (FAddIx + 1) mod Length(FData);
      if (FAddedValues < Length(FData)) then
        Inc(FAddedValues);
      FCurrentAverage := FSum div FAddedValues;
    end;
    
    function TMovingAverage.Average: Integer;
    begin
      Result := FCurrentAverage;
    end;
    
    constructor TMovingAverage.Create(length: integer);
    var
      i : integer;
    begin
      SetLength( FData,length);
      for i := 0 to length - 1 do
        FData[i] := 0;
      FSum := 0;
      FCurrentAverage := 0;
      FAddIx := 0;
      FAddedValues := 0;
    end;
    

    【讨论】:

    • +1 用于 TStopWatch。请注意,在某些计算机上,我实际上发现 TStopWatch 的底层 Win32 API 函数不稳定并且无法正常工作(Windows XP,在某些具有 ICH4/ICH5 时代芯片组的 Intel Pentium 4 芯片组上)。在现代硬件(Core2Duo 及更高版本)上不再是问题,但是......我花了很多时间测量毫秒(试图准确地做到这一点),而 Windows API 让我为尝试而感到难过。 (我并不是说我想要硬实时,只是一个不会冻结或向前倾斜的毫秒计数器)- TStopWatch 比 GetTickCount 准确得多!
    • +1 确实如此,现在如果我也能考虑文件大小就好了.......虽然故事完全不同............
    【解决方案2】:

    您可以使用TTimeSpan 记录代替TDateTime,您可以创建一个新实例,将经过的刻度传递给constructor,然后您可以使用天、小时、分钟、秒和毫秒属性来显示经过的时间。现在计算剩余时间,您需要下载的总字节数和当前下载的字节数。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-14
      • 2012-02-08
      • 2012-12-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多