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;
答案 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);