I am new to Delphi (been programming in it for about 6 months now). So far, it's been an extremely frustrating experience, most of it coming from how bad Delphi is at handling dates and times. Maybe I think it's bad because I don't know how to use TDate and TTime properly, I don't know. Here is what is happening on me right now :
// 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'))));
That's not the exact code I use, everything is in variables and used in another context, but I think you can see the problem. Why is that calculation wrong? How am I suppose to work around this problem?
Given
your code, using
MinutesBetween
, effectively does this:However, it might be better to round:
What is actually the floating-point value?
so you are clearly suffering from traditional floating-point problems here.
Update:
The major benefit of
Round
is that if the minute span is very close to an integer, then the rounded value will guaranteed be that integer, while the truncated value might very well be the preceding integer.The major benefit of
Trunc
is that you might actually want this kind of logic: Indeed, if you turn 18 in five days, legally you are still not allowed to apply for a Swedish driving licence.So you if you'd like to use
Round
instead ofTrunc
, you can just addto your unit. Then the identifier
MinutesBetween
will refer to this one, in the same unit, instead of the one inDateUtils
. The general rule is that the compiler will use the function it found latest. So, for instance, if you'd put this function above in your own unitDateUtilsFix
, thenwill use the new
MinutesBetween
, sinceDateUtilsFix
occurss to the right ofDateUtils
.Update 2:
Another plausible approach might be
This will return
round(spn)
is the span is within the fuzz range of an integer, andtrunc(spn)
otherwise.For example, using this approach
will yield 0 minutes, just like the original
trunc
-based version, and just like the Swedish Trafikverket would like. But it will not suffer from the problem that triggered the OP's question.This is an issue that is resolved in the latest versions of Delphi. So you could either upgrade, or simply use the new code in Delphi 2010. For example this program produces the output you expect:
The Delphi 2010 code for
MinutesBetween
looks like this:So,
MinutesBetween
effectively boils down to a floating point subtraction of the two date/time values. Because of the inherent in-exactness of floating point arithmetic, this subtraction can yield a value that is slightly above or below the true value. When it is below the true value, the use ofTrunc
will take you all the way down to the previous minute. Simply replacingTrunc
withRound
would resolve the problem.As it happens the latest Delphi versions, completely overhaul the date/time calculations. There are major changes in
DateUtils
. It's a little harder to analyse, but the new version relies onDateTimeToTimeStamp
. That converts the time portion of the value to the number of milliseconds since midnight. And it does so like this:Note the use of
Round
. The use ofRound
rather thanTrunc
is the reason why the latest Delphi code handlesMinutesBetween
in a robust fashion.Assuming that you cannot upgrade right now, I would deal with the problem like this:
MinutesBetween
etc.MinutesBetween
etc. will now work.MinutesBetween
etc. with code hooks. When you do come to upgrade, you can simply remove the hooks.