【问题标题】:Ignore milliseconds in TDateTime (Same TDateTime values subtracted are not 0)忽略 TDateTime 中的毫秒数(减去的相同 TDateTime 值不为 0)
【发布时间】:2014-09-03 12:35:58
【问题描述】:

简而言之我的问题:TDateTime A (03.09.2014 13:40) - TDateTime B (03.09.2014 13:40) = -1

我有两个要比较的 TDateTime 值,首先我使用 = 运算符检查它们是否相同,但经过几次测试后,我意识到这不适用于我的情况。令人困惑的是,它在大多数时候都很好用,但有时却不行。

我从现有文件的LastWriteTime 属性中获取一个值,而另一个值来自 MySQL 数据库。

这是一些代码:

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;

现在正如之前提到的,这个简单的代码大部分时间都在工作,但是对于某些 TDateTime 值,我得到一个否定的结果,这应该意味着我来自 MySQL 数据库的 TDateTime 值比我的本地文件 TDateTime 更早价值。

于是我开始调试:

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

这为我提供了自 1899 年 12 月 30 日以来经过的天数以及时间的十进制值。

示例值:

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

不一样的小数应该是毫秒还是不是(从xxxx,4875开始)? 现在,如果我比较它们(例如CompareDateTime(a,b)a = b),我不会得到0/true(我会比较aTicksbTicks 的值)。

我是否必须以获取本地文件TDateTime 的方式进行更改(目前我正在使用 WinAPI,GetLastWriteTimeUTC 没有为我提供正确的 UTC 时间)?

我认为这不是一个真正的难题,但我不知道如何解决这个问题。 ``TDateTime 是否存储隐藏的毫秒数?在调试模式下,我看不到任何毫秒,我不知道如何从我的TDateTime 中获取这个值(使用 Delphi XE2)。

这里有一些关于我的项目的额外细节

我通过这种方式获得TDateTime b

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;

如果 MySQL 数据库中的文件“较新”,我将其替换并设置我的 MySQL TDateTime a 属性中的LastWriteTime 属性:SetLastWriteTimeUTC(a)(以及我的 MySQL (a) 中的 TDateTime 值)没有任何毫秒值)。所以问题应该不会再次发生,但确实会发生。

我的 MySQL 数据库上的 TDateTime 值来自此

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)

我希望这是足够的信息,而不是太多

最好的问候,

尼古拉斯

更新:

代码与我的主程序“相同”,正如我上面所说,错误的 DateTime 比较不会一直只在某些文件上触发(在我的例子中是 $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;

【问题讨论】:

  • 为什么不忽略毫秒?
  • 我不知道怎么做,也没有找到任何方法来做到这一点——它可以解决我的问题,如果你能提供我的代码,我会很高兴的,所以我可以检查一下。但如果知道为什么会这样也很好?
  • 请你给出一个简短的完整程序来演示这个问题。
  • 我将创建一个并将其上传到文件主机或云空间,但我认为这并不代表我自己现有的“特殊”问题。如上所述,在大多数情况下,它运行良好。我比较了我的 MySQL 和本地文件中的 TDateTime 值。但是有几个文件(每次都一样)不是“可比的”,那么情况就如上面解释的那样。我删除了这些文件并使用我的 MySQL 数据库中的数据重新创建它们,但它仍然会发生。编辑:我想我知道如何“重新创建”它,并将为您提供相同的文件等。请等待几分钟或更长时间。
  • 那个程序对我们没有用,因为我们没有你的文件。你不能给我们一些我们可以运行的东西吗?

标签: mysql delphi compare milliseconds tdatetime


【解决方案1】:

如果您想比较两个TDateTime 值并匹配第二个值,忽略毫秒差异,请使用DateUtils 单位中的SecondsBetween

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.

如果需要匹配其他分辨率,还有DaysBetweenMinutesBetweenMilliSecondsBetween等其他差异的类似功能。这是一个适用于各种分辨率(精确匹配、日、小时、分钟或秒)的实用程序函数:

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;

使用示例:

  dtOne := 41884.4875115741;
  dtTwo := 41884.4875153356;

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

【讨论】:

  • 或者,您可以简单地重新编码 TDateTime 值以删除毫秒。看看System.DateUtils.RecodeMilliSecond()。或者直接使用EncodeDate()EncodeTime()而不是SystemTimeToDateTime(),这样你就可以从一开始就将毫秒设置为0。
  • @Remy:是的,这些也是可行的选择。我选择这种方式是因为它很容易扩展到忽略微小差异或小时差异的工作(通过添加分辨率类型作为参数)并且因为它很容易编写。 :-)
  • 这是一个聪明的方法。谢谢你,它对我来说很好用!
  • 对不起,我有一个与SecondsBetween 比较的问题,这个方法不比较小时、分钟和天等,我说得对吗?只有几秒钟?在我的测试用例中,它确实给了我一个结果,即我昨天的文件比我当前的文件新,因为秒数更大。当我几个小时前评论你的帖子时,我只用今天的文件对其进行了测试。
  • @Askinator:它比较之间的秒数,如果秒数不匹配将大于零;如果分钟不匹配将更大(一整分钟将返回 60 秒之间),一个小时将更大(60 * 60 = 3600 秒之间)等。完整的 24 小时差异将导致 86400 秒之间.所以它将值的每个部分都比较到秒,但忽略毫秒。如果要包含毫秒,请改用MillisecondsBetween
【解决方案2】:

您还可以通过将TDateTime 转换为字符串并进行比较来进行游戏。通过这种方式,您可以设置时间格式来制作更复杂的条件。例如,您可以检查日期是否在同一小时内,忽略分钟和秒:

If FormatDateTime('yyyymmddhh', Date1) = FormatDateTime('yyyymmddhh', Date2) ...

另一种方法是对它们进行解码并比较您感兴趣的部分,如:

DecodeDateTime (Date1, Y1, M1, D1, H1, N1, S1, mS1);
DecodeDateTime (Date2, Y2, M2, D2, H2, N2, S2, mS2);
If (Y1 = Y2) and (M1 = M2) and (D1 = D2) and (H1 = H2) then ...

【讨论】:

    【解决方案3】:
        dt := Now;
        dtWithoutMilliseconds := SecondsBetween(0,dt)*OneSecond;
        //or
        dtWithoutMilliseconds := dt-MillisecondOf(dt)*OneMillisecond;
        //or
        dtWithoutMilliseconds := Trunc(dt)+Trunc(Frac(dt)*SecsPerDay)/SecsPerDay;
        //or simply
        dtWithoutMilliseconds := Trunc(dt)+Trunc(Frac(dt)*3600)/3600;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-23
      • 1970-01-01
      相关资源
      最近更新 更多