【问题标题】:1 minute Error in time difference calculation1分钟时差计算错误
【发布时间】:2016-12-12 21:31:55
【问题描述】:

我的编程环境是 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!

这是为什么?

【问题讨论】:

  • 我无法在 C++Builder 10.1 Berlin 中重现这一点。 C++Builder 6 相当古老。可能是一个错误,但这样一个老错误被修复的机会是零。

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


【解决方案1】:

这是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(),它返回一个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)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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-03
    • 2019-09-28
    • 2023-01-30
    • 2018-03-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多