这是DateUtils 单元的旧版本中的一个已知问题,该单元在 Delphi/C++Builder 6 中首次引入。该问题持续了数年,直到最终在 Delphi/C++Builder XE 中得到修复.
TDateTime 本质上只是一个double,其中日期存储在整数部分,时间存储在小数部分。因此,它需要进行近似表示和四舍五入。
在您的示例中,dtEnter 是 42703.0340277778,dtExit 是 42703.0347222222。
两个TDateTime 值之间的跨度是使用简单的浮点数学计算得出的:
function SpanOfNowAndThen(const ANow, AThen: TDateTime): TDateTime;
begin
if ANow < AThen then
Result := AThen - ANow
else
Result := ANow - AThen;
end;
在您的示例中,SpanOfNowAndThen(dtEnter, dtExit) 是 0.000694444439432118。
在MinutesBetween()函数的情况下,在XE之前它会调用MinuteSpan(),它返回一个Double,它是SpanOfNowAndThen()乘以MinsPerDay常量的结果,然后它会截断关闭小数部分以产生最终整数:
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;
在您的示例中,MinuteSpan() 生成的十进制值略小于 1.0(准确地说是0.99999999278225),当小数点被截断时变为 0。
在 XE 中,许多 DateUtils 函数被重新编写,以使用不基于浮点数学的更可靠的计算。尽管MinuteSpan() 还是一样,MinutesBetween() 不再使用MinuteSpan()。相反,它现在将两个 TDateTime 值转换为毫秒(这是无损的,因为 TDateTime 具有毫秒精度),减去这些值,然后将差的绝对值除以每分钟的恒定毫秒数:
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;
在您的示例中,DateTimeToMilliseconds(dtEnter) 是 63616063740000 和 DateTimeToMilliseconds(dtExit) 是 63616063800000,所以区别是 60000 毫秒,正好是 1 分钟。
对于 XE 之前的版本,您必须在自己的代码中手动实施类似的修复。这在各种在线博客中都有讨论,例如:
How do I work around Delphi's inability to accurately handle datetime manipulations?
Accurate Difference Between Two TDateTime Values