1分钟时差计算错误

时间:2016-12-12 21:31:55

标签: c++builder c++builder-6

我的编程环境是Borland C ++ Builder 6。

我在以下代码中遇到了错误结果的问题:

TDateTime dtEnter, dtExit;

dtEnter = EncodeDateTime(2016, 11, 29, 0, 49, 0, 0);
dtExit = EncodeDateTime(2016, 11, 29, 0, 50, 0, 0);

ShowMessage(dtEnter);
ShowMessage(dtExit);
ShowMessage(IntToStr(MinutesBetween(dtEnter, dtExit)));

结果为0而不是1!

为什么会这样?

1 个答案:

答案 0 :(得分:0)

这是DateUtils单元的旧版本中的一个已知问题,它最初是在Delphi / C ++ Builder 6中引入的。这个问题持续了几年,直到最终在Delphi / C ++中修复Builder XE。

TDateTime基本上只是double,其中日期存储在整数部分中,时间存储在小数部分中。因此,它需要进行近似表示和舍入。

在您的示例中,dtEnter42703.0340277778dtExit42703.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(),它会返回DoubleSpanOfNowAndThen()的结果乘以{MinsPerDay 1}}常量,然后它将截断小数部分以产生最终整数:

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)63616063740000DateTimeToMilliseconds(dtExit)63616063800000,因此差异为60000毫秒,正是1分钟。

对于XE之前的版本,您必须在自己的代码中手动实现类似的修复。这将在各种在线博客中讨论,例如:

How do I work around Delphi's inability to accurately handle datetime manipulations?

Accurate Difference Between Two TDateTime Values