在Delphi中:如何将TDateTime四舍五入到最接近的秒,分钟,五分钟等?

时间:2010-11-08 08:21:26

标签: delphi datetime

Delphi中是否存在将TDateTime值舍入到最接近的秒,最近的小时,最近的5分钟,最近的半小时等的例程?

更新:

加布尔提供了答案。有一些小错误,可能是由于完全缺乏测试; - )

我清理了一下并测试了它,这是最终的(?)版本:

function RoundDateTimeToNearestInterval(vTime : TDateTime; vInterval : TDateTime = 5*60/SecsPerDay) : TDateTime;
var
  vTimeSec,vIntSec,vRoundedSec : int64;
begin
  //Rounds to nearest 5-minute by default
  vTimeSec := round(vTime * SecsPerDay);
  vIntSec := round(vInterval * SecsPerDay);

  if vIntSec = 0 then exit(vTimeSec / SecsPerDay);

  vRoundedSec := round(vTimeSec / vIntSec) * vIntSec;

  Result := vRoundedSec / SecsPerDay;
end;

8 个答案:

答案 0 :(得分:8)

类似的东西(完全未经测试,直接在浏览器中编写):

function RoundToNearest(time, interval: TDateTime): TDateTime;
var
  time_sec, int_sec, rounded_sec: int64;
begin
  time_sec := Round(time * SecsPerDay);
  int_sec := Round(interval * SecsPerDay);
  rounded_sec := (time_sec div int_sec) * int_sec;
  if (rounded_sec + int_sec - time_sec) - (time_sec - rounded_sec) then
    rounded_sec := rounded_sec + time_sec;
  Result := rounded_sec / SecsPerDay;
end;

代码假设您希望以第二精度进行舍入。毫秒被扔掉了。

答案 1 :(得分:7)

哇!伙计们,你怎么这么简单太复杂的东西......你们大多数人都没有选择将其舍入到最接近的1/100秒等等......

这个更简单,也可以舍入到毫秒部分:

function RoundToNearest(TheDateTime,TheRoundStep:TDateTime):TdateTime;
    begin
         if 0=TheRoundStep
         then begin // If round step is zero there is no round at all
                   RoundToNearest:=TheDateTime;
              end
         else begin // Just round to nearest multiple of TheRoundStep
                   RoundToNearest:=Round(TheDateTime/TheRoundStep)*TheRoundStep;
              end;
    end;

你可以用这个常见或不常见的例子来测试它:

// Note: Scroll to bottom to see examples of round to 1/10 of a second, etc

// Round to nearest multiple of one hour and a half (round to 90'=1h30')
ShowMessage(FormatDateTime('hh:nn:ss.zzz'
                          ,RoundToNearest(EncodeTime(15,31,37,156)
                                         ,EncodeTime(1,30,0,0))
                          )
           );

// Round to nearest multiple of one hour and a quarter (round to 75'=1h15')
ShowMessage(FormatDateTime('hh:nn:ss.zzz'
                          ,RoundToNearest(EncodeTime(15,31,37,156)
                                         ,EncodeTime(1,15,0,0))
                          )
           );

// Round to nearest multiple of 60 minutes (round to hours)
ShowMessage(FormatDateTime('hh:nn:ss.zzz'
                          ,RoundToNearest(EncodeTime(15,31,37,156)
                                         ,EncodeTime(1,0,0,0))
                          )
           );

// Round to nearest multiple of 60 seconds (round to minutes)
ShowMessage(FormatDateTime('hh:nn:ss.zzz'
                          ,RoundToNearest(EncodeTime(15,31,37,156)
                                         ,EncodeTime(0,1,0,0))
                          )
           );

// Round to nearest multiple of second (round to seconds)
ShowMessage(FormatDateTime('hh:nn:ss.zzz'
                          ,RoundToNearest(EncodeTime(15,31,37,156)
                                         ,EncodeTime(0,0,1,0))
                          )
           );

// Round to nearest multiple of 1/100 seconds
ShowMessage(FormatDateTime('hh:nn:ss.zzz'
                          ,RoundToNearest(EncodeTime(15,31,37,141)
                                         ,EncodeTime(0,0,0,100))
                          )
           );

// Round to nearest multiple of 1/100 seconds
    ShowMessage(FormatDateTime('hh:nn:ss.zzz'
                          ,RoundToNearest(EncodeTime(15,31,37,156)
                                         ,EncodeTime(0,0,0,100))
                          )
           );

// Round to nearest multiple of 1/10 seconds
    ShowMessage(FormatDateTime('hh:nn:ss.zzz'
                          ,RoundToNearest(EncodeTime(15,31,37,151)
                                         ,EncodeTime(0,0,0,10))
                          )
           );

// Round to nearest multiple of 1/10 seconds
    ShowMessage(FormatDateTime('hh:nn:ss.zzz'
                          ,RoundToNearest(EncodeTime(15,31,37,156)
                                         ,EncodeTime(0,0,0,10))
                          )
           );

希望这有助于像我这样的人,需要四舍五入到1 / 100,1 / 25或1/10秒。

答案 2 :(得分:5)

如果你想RoundUp或RoundDown ...就像Ceil和Floor ......

这里有(不要忘记将Math单元添加到您的uses子句中):

function RoundUpToNearest(TheDateTime,TheRoundStep:TDateTime):TDateTime;
    begin
         if 0=TheRoundStep
         then begin // If round step is zero there is no round at all
                   RoundUpToNearest:=TheDateTime;
              end
         else begin // Just round up to nearest bigger or equal multiple of TheRoundStep
                   RoundUpToNearest:=Ceil(TheDateTime/TheRoundStep)*TheRoundStep;
              end;
    end;

function RoundDownToNearest(TheDateTime,TheRoundStep:TDateTime):TDateTime;
    begin
         if 0=TheRoundStep
         then begin // If round step is zero there is no round at all
                   RoundDownToNearest:=TheDateTime;
              end
         else begin // Just round down to nearest lower or equal multiple of TheRoundStep
                   RoundDownToNearest:=Floor(TheDateTime/TheRoundStep)*TheRoundStep;
              end;
    end;

当然还有一个小的改动(使用Float类型而不是TDateTime类型)如果也可以用于Round,RoundUp和RoundDown十进制/浮点值到十进制/浮点步骤。

他们是:

function RoundUpToNearest(TheValue,TheRoundStep:Float):Float;
    begin
         if 0=TheRoundStep
         then begin // If round step is zero there is no round at all
                   RoundUpToNearest:=TheValue;
              end
         else begin // Just round up to nearest bigger or equal multiple of TheRoundStep
                   RoundUpToNearest:=Ceil(TheValue/TheRoundStep)*TheRoundStep;
              end;
    end;

function RoundToNearest(TheValue,TheRoundStep:Float):Float;
    begin
         if 0=TheRoundStep
         then begin // If round step is zero there is no round at all
                   RoundToNearest:=TheValue;
              end
         else begin // Just round to nearest multiple of TheRoundStep
                   RoundToNearest:=Floor(TheValue/TheRoundStep)*TheRoundStep;
              end;
    end;

function RoundDownToNearest(TheValue,TheRoundStep:Float):Float;
    begin
         if 0=TheRoundStep
         then begin // If round step is zero there is no round at all
                   RoundDownToNearest:=TheDateTime;
              end
         else begin // Just round down to nearest lower or equal multiple of TheRoundStep
                   RoundDownToNearest:=Floor(TheValue/TheRoundStep)*TheRoundStep;
              end;
    end;

如果你想在同一个单元上使用这两种类型(TDateTime和Float)...在接口部分添加过载指令,例如:

function RoundUpToNearest(TheDateTime,TheRoundStep:TDateTime):TDateTime;overload;
function RoundToNearest(TheDateTime,TheRoundStep:TDateTime):TDateTime;overload;
function RoundDownToNearest(TheDateTime,TheRoundStep:TDateTime):TDateTime;overload;

function RoundUpToNearest(TheValue,TheRoundStep:Float):Float;overload;
function RoundToNearest(TheValue,TheRoundStep:Float):Float;overload;
function RoundDownToNearest(TheValue,TheRoundStep:Float):Float;overload;

答案 3 :(得分:2)

这是一个具有可调精度的未经测试的代码。

Type
  TTimeDef = (tdSeconds, tdMinutes, tdHours, tdDays)

function ToClosest( input : TDateTime; TimeDef : TTimeDef ; Range : Integer ) : TDateTime
var 
  Coeff : Double;
RInteger : Integer;
DRInteger : Integer;
begin
  case TimeDef of
    tdSeconds :  Coeff := SecsPerDay;  
    tdMinutes : Coeff := MinsPerDay;
    tdHours : Coeff :=  MinsPerDay/60;
    tdDays : Coeff := 1;
  end;

  RInteger := Trunc(input * Coeff);
  DRInteger := RInteger div Range * Range
  result := DRInteger / Coeff;
  if (RInteger - DRInteger) >= (Range / 2) then
    result := result + Range / Coeff;

end;

答案 4 :(得分:2)

尝试使用DateUtils单位。
但要在一分钟,一小时甚至秒钟内进行舍入,只需解码然后对日期值进行编码,并将毫秒,秒和分钟设置为零。舍入到分钟或小时的倍数只意味着:解码,向上或向下小时或分钟,然后再次编码。
要编码/解码时间值,请使用SysUtils中的EncodeTime / DecodeTime。使用EncodeDate / DecodeDate作为日期。应该可以使用所有这些来创建自己的舍入函数。
另外,SysUtils函数具有常量,如MSecsPerDay,SecsPerDay,SecsPerMin,MinsPerHour和HoursPerDay。时间基本上是午夜过后的毫秒数。您可以使用MSecsPerDay来精确地计算Frac(时间),这是精确的毫秒数。
不幸的是,由于时间值是​​浮点数,因此总有可能出现小的舍入错误,因此您可能无法获得预期值...

答案 5 :(得分:0)

这是一个非常有用的代码,我使用它是因为我发现日期时间往往会“漂移”,如果你将它增加数小时或分钟多次,如果你正在努力达到一个严格的话就会搞砸了时间序列。例如,00:00:00.000变为23:59:59.998 我实现了Sveins版本的Gabrs代码,但我提出了一些修正:默认值对我不起作用,退出后的'(vTimeSec / SecsPerDay)'我认为是错误的,它不应该存在。我的代码有更正&评论,是:

    Procedure TNumTool.RoundDateTimeToNearestInterval
                        (const ATime:TDateTime; AInterval:TDateTime{=5*60/SecsPerDay}; Var Result:TDateTime);
    var                                            //Rounds to nearest 5-minute by default
      vTimeSec,vIntSec,vRoundedSec : int64;     //NB datetime values are in days since 12/30/1899 as a double
    begin
      if AInterval = 0 then
        AInterval := 5*60/SecsPerDay;                 // no interval given - use default value of 5 minutes
      vTimeSec := round(ATime * SecsPerDay);          // input time in seconds as integer
      vIntSec  := round(AInterval * SecsPerDay);      // interval time in seconds as integer
      if vIntSec = 0 then
        exit;                                           // interval is zero -cannot round the datetime;
      vRoundedSec := round(vTimeSec / vIntSec) * vIntSec;   // rounded time in seconds as integer
      Result      := vRoundedSec / SecsPerDay;              // rounded time in days as tdatetime (double)
    end;

答案 6 :(得分:0)

如果有人在帖子中读到了很深的话,那么这是另一个想法。正如z666zz666z所说,它不必很复杂。 Delphi中的TDateTime是双精度浮点数,其中整数部分表示日期。如果将舍入值作为一天中“期间”的数目传递,则舍入函数将简单地为:Round(dt * RoundingValue)/ RoundingValue。该方法将是:

procedure RoundTo(var dt: TDateTime; RoundingValue:integer);
    begin
    if RoundingValue > 0 then
        dt := Round(dt * RoundingValue) / RoundingValue;
    end;

示例:

RoundTo(targetDateTime, SecsPerDay); // round to the nearest second
RoundTo(targetDateTime, SecsPerDay div 10); // round to the nearest 10 seconds
RoundTo(targetDateTime, MinsPerDay); // round to the nearest minute
RoundTo(targetDateTime, MinsPerDay div 5); // round to the nearest five minutes
RoundTo(targetDateTime, HoursPerDay); // round to the nearest hour

它甚至可以满足第二个四舍五入的要求:

RoundTo(targetDateTime, SecsPerDay * 10); // round to the nearest 1/10 second

答案 7 :(得分:0)

最简单的(四舍五入到分钟):

DateTime := OneMinute * Round(DateTime / OneMinute);