如何将毫秒转换成TDateTime?

5

我正在执行一个下载数千个文件的长循环。由于可能需要几个小时,因此我希望显示预计剩余时间。但是,根据我编写的代码,我得到的是平均毫秒数。如何将这个从毫秒转换为 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 - 我愿意尝试不同的方法来计算最后20个(或AVG_BASE)下载的平均速度,因为我知道我的字符串列表解决方案并不是最好的。我不想基于所有下载来计算它,因为速度可能会在那段时间内发生变化。因此,我只检查最近的20个。

2个回答

11

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 

要计算平均时间,请考虑使用此 移动平均 记录:

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
+1 给 TStopWatch。请注意,在某些计算机上,我实际上发现 TStopWatch 的底层 Win32 API 函数不稳定,不能正常工作(在具有 ICH4/ICH5 时代芯片组的某些 Intel Pentium 4 芯片组上的 Windows XP 上)。在现代硬件(Core2Duo 及更高版本)上不再是问题,但是……我花了很多时间来测量毫秒数(试图准确地做到这一点),Windows API 因此给了我很多痛苦。(我并不是说我想要硬实时,只是想要一个不会冻结或突然向前跳的毫秒计数器)--TStopWatch 比 GetTickCount 更准确! - Warren P
+1 确实如此,现在如果我也能考虑文件大小的话……不过这是完全不同的故事了…… - Jerry Dodge

8

您可以使用TTimeSpan记录来代替TDateTime,创建一个新的实例并将经过的滴答数传递给构造函数,然后可以使用Days、Hours、Minutes、Seconds和Milliseconds属性来显示经过的时间。现在,要计算剩余时间,您需要总下载字节数和当前已下载字节数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接