How to check TDateTime contents are valid

2.8k Views Asked by At

Is there a way to check whether a Delphi TDateTime variable has valid contents without trapping exceptions on conversions?

I have written an application that has to parse through hundreds of huge files written as raw Delphi records that contain a TDateTime field. Occasionally I get a record where the contents has a TDateTime value like 2.0927117954e+262 which gives a Floating point invalid Operation exception when passed to conversion routines. I know I can simply trap the exception but this is such a nuisance when debugging as the debugger keeps stopping and I want to keep it enabled in case of other errors.

3

There are 3 best solutions below

0
On

If your issue is the debugger stopping, you can fix that in the IDE of later Delphi versions. Set two breakpoints before and after the line where the exception will occur. E.g. in Delphi 2007, right-click on the margin red dot for the first BP, choose BreakPoint properties/Advanced/Ignore subsequent exceptions. On the second BP, 'Handle subsequent exceptions'

0
On

Your example value is obviously out of valid DateTime range. You can test if it is in range before performing any conversion.

var
  d: TDateTime;
..

d := 2.0927117954e+262;
if (d <= MaxDateTime) and (d >= MinDateTime) then
  s := DateTimeToStr(d)
else
  ..
2
On

Reading your problem description, I conclude that it's absolutely valid for your application to go on reading the file if some record has a corrupt/invalid time value and so you want so skip it (maybe mark or collect the problem otherwise).

The VCL function System.SysUtils.TryFloatToDateTime is made exactly for this purpose, and using it seems by far the best option to me.

Delphi example:

uses SysUtils;

function IsValidDateTime(dt: Double): Boolean;
var
  dummy: TDateTime;
begin
  Result := TryFloatToDateTime(dt, dummy);
end;

C++ Builder example:

#include <SysUtils.hpp>

bool isValidDateTime(const double dt) 
{
    TDateTime dummy;
    return TryFloatToDateTime(dt, dummy);
}

This function is not only handy (because exception-free and already there) but also very readable, it also follows a nice convention that runs through the entire framework:

  • name begins with Try saying that it can fail
  • Boolean direct results to indicate a valid operation
  • indirect result gets the actual value (if operation succeeded)