mysqldelphicomparemillisecondstdatetime

Ignore milliseconds in TDateTime (Same TDateTime values subtracted are not 0)


my problem in short: TDateTime A (03.09.2014 13:40) - TDateTime B (03.09.2014 13:40) = -1

I have two TDateTime values which i want to compare, first i used the = operator to check if they are the same but after a few tests i realized that this is not working in my case. The confusion in this is that it works great the most time but sometimes not.

I get one value out of the LastWriteTime attribute from an existing file and the other value is from a MySQL database.

Here is some code:

TDateTime a := FileList[loop].Lastwritetime.AsUTCDateTime; // TDateTime from MySQL
TDateTime b := GetLastwritetimeUtc(Sourcedirectory); // TDateTime from my local file

if (CompareDateTime(a, b) = 0) then
begin
   // do some stuff.
end;

Now as mentioned before this easy code is working most of the time but for some TDateTime values i get a negative result which should mean that my TDateTime value from the MySQL database is earlier then my local file TDateTime value.

So i started debugging:

double aTicks := a; // MySQL TDateTime
double bTicks := b; // Local file TDateTime

this provides me the days passed since 30.12.1899 and the decimal values the time.

Example values:

// a = 02.09.2014 11:42:01
// b = 02.09.2014 11:42:01
// aTicks = 41884,4875115741
// bTicks = 41884,4875153356

The decimals which are not same should be milliseconds or not (starting after xxxx,4875)? Now if i compare them (e.g. CompareDateTime(a,b) or a = b) i do not get 0/true (i do not compare the aTicks and bTicks values).

Do i have to make changes in my way i get the local file TDateTime (at the moment i am using the WinAPI, the GetLastWriteTimeUTC did not provide me the correct UTC time)?

I think it is not really a hard problem but i have no idea how to solve this. Does the `` TDateTime store the milliseconds hidden? In the debugging mode i do not see any milliseconds and i do not know how to get this value out of my TDateTime (using Delphi XE2).

Here are some extra details about my project

I get the TDateTime b value this way

function GetLastwritetimeUtc(source: String): TDateTime;
var
   fad: TWin32FileAttributeData;
   SystemTime: TSystemTime;
   lastwritetimeUtc: TDateTime;
begin
   GetFileAttributesEx(PWideChar(source),GetFileExInfoStandard,@fad);
   FileTimeToSystemTime(fad.ftLastWriteTime, SystemTime);
   lastwritetimeUtc := SystemTimeToDateTime(SystemTime);
   result := lastwritetimeUtc;
end;

If a file from the MySQL database is "newer" i replace it and set the LastWriteTime from my MySQL TDateTime a attribute this way: SetLastWriteTimeUTC(a) (and my TDateTime values from the MySQL (a) do not have any milliseconds value). So the problem should not occure again but it does.

The TDateTime value on my MySQL database is from this

XSDateTime c := DateTimeToXSDateTime(GetLastwritetimeUtc(sourceDirectory));
// i send this via WCF service to the MySQL database and store it in a `TDateTime` column (which does not include milliseconds)

I hope this is enough information and not too much.

Best regards,

Niclas

UPDATE:

The code does "the same" as my main program, as i said above the wrong DateTime comparision does not trigger all the time only on some files (in my case the $Default10.dsk).

uses
  SysUtils,
  Soap.SoapHttpTrans,
  DateUtils,
  Windows,
  System.IOUtils,
  Soap.XSBuiltIns;

var
  fad: TWin32FileAttributeData;
  SystemTime: TSystemTime;
  lastwritetimeUtcA: TDateTime;
  lastwritetimeUtcB: TDateTime;
  sourceFileA: string;
  sourceFileB: string;
  lastwritetimeXS: TXSDateTime;
begin
  while True do
  begin
    sourceFileA := 'Path to a file on your computer no matter which';
    sourceFileB := 'Path to another file on your computer no matter which';

    //GetLastWriteTime from local file
    GetFileAttributesEx(PWideChar(sourceFileA),GetFileExInfoStandard,@fad);
    FileTimeToSystemTime(fad.ftLastWriteTime, SystemTime);
    lastwritetimeUtcA := SystemTimeToDateTime(SystemTime);

    //Set the localfile lastwritetime to the theoretical mySQL file
    // in my main program there does not exist a mySQL file (only a value of TDateTime in the TDateTime column of my database where i store the lastwritetime from the local files
    TFile.SetLastWriteTimeUtc(sourceFileB, lastwritetimeUtcA);

    //Get the LastWriteTime from theoretical mySQL file
    // in my main program i get the lastwritetime value from the MySQL database via WCF that is the reason for the convertion of XSDateTime.AsUTCDateTime and DateTimeToXSDateTime
    GetFileAttributesEx(PWideChar(sourceFileB),GetFileExInfoStandard,@fad);
    FileTimeToSystemTime(fad.ftLastWriteTime, SystemTime);
    lastwritetimeUtcB := SystemTimeToDateTime(SystemTime);

    //Convert lastwritetime to XSDatetime - how i do it in my program
    lastwritetimeXS := DateTimeToXSDateTime(lastwritetimeUtcB);
    {Convert it back to DateTime}
    lastwritetimeUtcB := lastwritetimeXS.AsUTCDateTime;

    //Compare them
    if lastwritetimeUtcA = lastwritetimeUtcB then
      Writeln('Same time')
    else
      writeln('Not same time');
    Sleep(500);
  end;
end;

Solution

  • If you want to compare two TDateTime values and match to the second, ignoring millisecond differences, use SecondsBetween from the DateUtils unit:

    program Project1;
    
    uses
      SysUtils, DateUtils;
    
    var
      dtOne, dtTwo: TDateTime;
    
    begin
      dtOne := 41884.4875115741;
      dtTwo := 41884.4875153356;
    
      if SecondsBetween(dtOne, dtTwo) = 0 then
        WriteLn('Dates the same without MS')
      else
        WriteLn('Not the same dates.');
    
      ReadLn;
    end.
    

    There are similar functions for other differences, such as DaysBetween, MinutesBetween, and MilliSecondsBetween if you need to match to other resolutions. Here's a utility function that works for various resolutions (exact match, day, hour, minute, or second):

    type
      TDiffResolution = (tdrExact, tdrDay, tdrHour, tdrMin, tdrSec);
    
    function IsSameDateTime(dValOne, dValTwo: TDateTime;
      const Resolution: TDiffResolution = tdrSec): Boolean;
    begin
      case Resolution of
        tdrExact: Result := MillisecondsBetween(dValOne, dValTwo) = 0;
        tdrDay: Result := IsSameDay(dValOne, dValTwo);
        tdrHour: Result := HoursBetween(dValOne, dValTwo) = 0;
        tdrMin: Result := MinutesBetween(dValOne, dValTwo) = 0;
        tdrSec: Result := SecondsBetween(dValOne, dValTwo) = 0;
      else
        raise Exception.CreateFmt('Invalid resolution value (%d) provided.',
                                  [Ord(Resolution)]);
      end;
    end;
    

    Sample use:

      dtOne := 41884.4875115741;
      dtTwo := 41884.4875153356;
    
      if IsSameDateTime(dtOne, dtTwo, tdrSec) then
        WriteLn('Dates are the same.')
      else
        WriteLn('Dates are different.');
      ReadLn;