Truncating TDateTime values to minutes

Giganews Newsgroups
Subject:Truncating TDateTime values to minutes
Posted by: John Hansen (John.Hans…@aspect.com)
Date:Fri, 26 Sep 2008

At my company we have several library functions for manipulating and
converting TDateTime values into other types used throughout our software.
A long time ago we ran into the fact that you can't simply treat the integer
part of a TDateTime as the date part and the decimal part as the time part.
For example: 35967.99999999.

In this case, the date you get when you convert this TDateTime into a string
is 06/22/1998 which is the same string you get when you convert 35968 into a
string.  So we had to rewrite our utility functions to not simply truncate a
TDateTime but to decode and reencode the value.

When truncating a TDateTime down to the nearest minute we use a function
like this:

function WFMRoundTime(const ADateTime: TDateTime): TDateTime;
var
  H, M, S, MS: Word;
  Y, Mo, D: Word;
begin
  DecodeDateTime(ADateTime, Y, Mo, D, H, M, S, MS);
  Result := EncodeDateTime(Y, Mo, D, H, M, 0, 0);
end;

One of the third party component packages we use defines a similar utility
function that is used deep in the bowels of their component and until we
reported the bug to them it was written like this:

const
  HourToTime  = (MinsPerHour * SecsPerMin * MSecsPerSec) / MSecsPerDay;
  MinuteToTime = (SecsPerMin * MSecsPerSec) / MSecsPerDay;

function OldBadRoundTime(const ADateTime: TDateTime): TDateTime;
var
  H, M, S, MS: Word;
begin
  DecodeTime(ADateTime, H, M, S, MS);
  Result := Int(ADateTime) + H * HourToTime + M * MinuteToTime;
end;

Unfortunately, for many TDateTime values when you truncate it you get a
value that represents 12:00am on the previous day.  We sent them the
function as I show above (WFMRoundTime) and they "fixed" this bug in a
recent build like this:

function NewBadRoundTime(const ADateTime: TDateTime): TDateTime;
var
  H, M, S, MS: Word;
begin
  Result := DateTimeToTimeStamp(ADateTime).Date - DateDelta;
  DecodeTime(ADateTime, H, M, S, MS);
  if Result < 0 then
    Result := Result - (H * HourToTime + M * MinuteToTime)
  else
    Result := Result +  H * HourToTime + M * MinuteToTime;
end;

Can anyone explain why you would want to use this new function (which as far
as we can tell still fails for many TDateTime values) instead of decoding
and encoding as we do in our library function?  If the approach we use is
too slow, is there a faster way to do it right for all TDateTime values?  It
would be nice if the Delphi RTL provided the right implementation for this
type of operation so that we wouldn't have to try to persuade component
providers that they are doing it wrong.

John Hansen

Replies