mar*_*set 12 delphi time delphi-2010
我是Delphi的新手(现在已经编程了大约6个月).到目前为止,这是一次非常令人沮丧的经历,其中大部分来自Delphi处理日期和时间的糟糕程度.也许我认为这很糟糕,因为我不知道如何正确使用TDate和TTime,我不知道.以下是我现在正在发生的事情:
// This shows 570, as expected
ShowMessage(IntToStr(MinutesBetween(StrToTime('8:00'), StrToTime('17:30'))));
// Here I would expect 630, but instead 629 is displayed. WTF!?
ShowMessage(IntToStr(MinutesBetween(StrToTime('7:00'), StrToTime('17:30'))));
Run Code Online (Sandbox Code Playgroud)
这不是我使用的确切代码,一切都在变量中并在另一个上下文中使用,但我认为你可以看到问题.为什么这个计算错了?我怎么想解决这个问题?
And*_*and 20
特定
a := StrToTime('7:00');
b := StrToTime('17:30');
ShowMessage(FloatToStr(a));
ShowMessage(FloatToStr(b));
Run Code Online (Sandbox Code Playgroud)
你的代码使用MinutesBetween,有效地做到了这一点:
ShowMessage(IntToStr(trunc(MinuteSpan(a, b)))); // Gives 629
Run Code Online (Sandbox Code Playgroud)
但是,圆形可能更好:
ShowMessage(IntToStr(round(MinuteSpan(a, b)))); // Gives 630
Run Code Online (Sandbox Code Playgroud)
什么是浮点值?
ShowMessage(FloatToStr(MinuteSpan(a, b))); // Gives 630
Run Code Online (Sandbox Code Playgroud)
所以你明显在这里遇到传统的浮点问题.
更新:
主要的好处Round是,如果分钟跨度非常接近整数,则舍入值将保证为整数,而截断值可能很好地是前面的整数.
主要的好处Trunc是你可能真的想要这种逻辑:事实上,如果你在五天内满18岁,从法律上说你仍然不能申请瑞典驾驶执照.
所以,如果你想使用Round而不是Trunc,你可以添加
function MinutesBetween(const ANow, AThen: TDateTime): Int64;
begin
Result := Round(MinuteSpan(ANow, AThen));
end;
Run Code Online (Sandbox Code Playgroud)
到你的单位.然后标识符MinutesBetween将在同一个单元中引用此标识符,而不是中的标识符DateUtils.一般规则是编译器将使用它最新发现的函数.因此,例如,如果您将此功能放在您自己的单位中DateUtilsFix,那么
implementation
uses DateUtils, DateUtilsFix
Run Code Online (Sandbox Code Playgroud)
将使用新的MinutesBetween,因为DateUtilsFix发生在右边DateUtils.
更新2:
另一种看似合理的方法可能是
function MinutesBetween(const ANow, AThen: TDateTime): Int64;
var
spn: double;
begin
spn := MinuteSpan(ANow, AThen);
if SameValue(spn, round(spn)) then
result := round(spn)
else
result := trunc(spn);
end;
Run Code Online (Sandbox Code Playgroud)
这将返回round(spn)是跨度在整数的模糊范围内,trunc(spn)否则.
例如,使用这种方法
07:00:00 and 07:00:58
Run Code Online (Sandbox Code Playgroud)
将产生0分钟,就像原始trunc版本一样,就像瑞典Trafikverket所希望的那样.但它不会受到触发OP问题的问题的困扰.
这是在最新版本的Delphi中解决的问题.因此,您可以升级,或者只是在Delphi 2010中使用新代码.例如,此程序生成您期望的输出:
{$APPTYPE CONSOLE}
uses
SysUtils, DateUtils;
function DateTimeToMilliseconds(const ADateTime: TDateTime): Int64;
var
LTimeStamp: TTimeStamp;
begin
LTimeStamp := DateTimeToTimeStamp(ADateTime);
Result := LTimeStamp.Date;
Result := (Result * MSecsPerDay) + LTimeStamp.Time;
end;
function MinutesBetween(const ANow, AThen: TDateTime): Int64;
begin
Result := Abs(DateTimeToMilliseconds(ANow) - DateTimeToMilliseconds(AThen))
div (MSecsPerSec * SecsPerMin);
end;
begin
Writeln(IntToStr(MinutesBetween(StrToTime('7:00'), StrToTime('17:30'))));
Readln;
end.
Run Code Online (Sandbox Code Playgroud)
Delphi 2010代码MinutesBetween如下所示:
function SpanOfNowAndThen(const ANow, AThen: TDateTime): TDateTime;
begin
if ANow < AThen then
Result := AThen - ANow
else
Result := ANow - AThen;
end;
function MinuteSpan(const ANow, AThen: TDateTime): Double;
begin
Result := MinsPerDay * SpanOfNowAndThen(ANow, AThen);
end;
function MinutesBetween(const ANow, AThen: TDateTime): Int64;
begin
Result := Trunc(MinuteSpan(ANow, AThen));
end;
Run Code Online (Sandbox Code Playgroud)
因此,MinutesBetween有效归结为两个日期/时间值的浮点减法.由于浮点运算固有的精确性,该减法可以产生略高于或低于真值的值.当它低于真实值时,使用Trunc将带您一直到前一分钟.简单地更换Trunc与Round就解决了问题.
正如最新的Delphi版本一样,彻底改变了日期/时间计算.有重大变化DateUtils.这有点难以分析,但新版本依赖于DateTimeToTimeStamp.这会将值的时间部分转换为自午夜以来的毫秒数.它是这样的:
function DateTimeToTimeStamp(DateTime: TDateTime): TTimeStamp;
var
LTemp, LTemp2: Int64;
begin
LTemp := Round(DateTime * FMSecsPerDay);
LTemp2 := (LTemp div IMSecsPerDay);
Result.Date := DateDelta + LTemp2;
Result.Time := Abs(LTemp) mod IMSecsPerDay;
end;
Run Code Online (Sandbox Code Playgroud)
注意使用Round.使用Round而不是Trunc最新的Delphi代码MinutesBetween以强大的方式处理的原因.
假设你现在无法升级,我会像这样处理问题:
MinutesBetween等MinutesBetween等的代码现在可以正常工作.MinutesBetween使用代码挂钩修复等.当你升级时,你可以简单地删除钩子.| 归档时间: |
|
| 查看次数: |
3272 次 |
| 最近记录: |