System.DateTime 比较 ('>') 或 ('<') 不符合我的预期

System.DateTime comparison ('>') or ('<') does not give what I expected

我想从文件列表中获取文件日期 > 今天截止日期的所有文件 - 所以,我有以下小代码

string[] MyFiles = Directory.GetFiles(MyConfig.pathTransmittedFiles, "*.adf")
        .Where(file => new FileInfo(file).LastWriteTime > dtCutOff).ToArray();

我的 collection 使用 dtCutOff == “{11/3/2015 1:33:26下午}”!所以'>'似乎没有用。

由于每天将文件送入队列一次,因此不需要精确到毫秒或类似的精度。因此 one-second TimeSpan 差异是可以接受的,可以使我的案例成功。

string[] MyFiles = Directory.GetFiles(MyConfig.pathTransmittedFiles, "*.adf")
  .Where(file => new FileInfo(file).LastWriteTime - TimeSpan.FromSeconds(1) > dtCutOff)
  .ToArray();

现在,当我的截止日期为“{11/3/2015 1:33:26 PM}”,而我的另一个修改日期为“{11/3/2015 1:33:27 PM}”的文件已按预期成功传递到我的 collection!!所以,它有效,这就是在所有这些建议之后它应该如何工作!谢谢ye-all.

看起来您的 Where 子句 lambda 可能不正确。试试这个。

string[] MyFiles = Directory.GetFiles(MyConfig.pathTransmittedFiles, "*.adf").Where(file => file.modified > dtCutOff).ToArray();

此代码有效:

var cutffDate = new DateTime(2015,1,1); // or whatever
var allFiles = Directory.GetFiles(MyConfig.pathTransmittedFiles, "*.adf");
var datedFiles = allFiles.Where(f => (new FileInfo(f)).LastWriteTime > cutffDate);

更新: 由于您的问题似乎与精度相关,您可以将比较更改为:

const long precision = 10; // vary this as needed
allFiles.Where(f =>
  (new FileInfo(f)).LastWriteTime.ToFileTime()/precision > cutffDate.ToFileTime()/precision);

或者您可以使用 ...LastAccessTime.Ticks/TimeSpan.TicksPerMillisecond 除此之外,您可能需要将所有 DateTime 值转换为 UTC(LastAccessTimeUtc 和 DateTime.UtcNow)以确保它不是一些奇怪的时区问题

首先,我会尝试 运行 它没有 Where 子句,只是为了确保您期望的所有文件确实是从 Directory.GetFiles 返回的初始数组的一部分。 date/time 比较完全有可能不是差异的根源。它可能与 Ivan 在问题评论中链接的问题更相关,或者可能与权限相关,或者其他一些事情。

接下来,请注意 DateTime 违反了 SRP,因为它有一个 Kind 属性,这是三个 DateTimeKind 枚举值之一.可以是 LocalUtcUnspecified

DateTime.Now 的情况下,Kind 将是 DateTimeKind.LocalFile.GetLastWriteTime 也 returns 它的价值与当地的一样。因此,如果您总是按照问题中显示的方式从 DateTime.Now 中导出 dtCutOff,那么它将 almost 始终是正确的比较函数。

"almost" 源于这样一个事实,即 DateTimeKind.Local 实际上可以在幕后代表两种不同的种类。也就是说,其实有四种种,只不过其中两种被一种暴露了。这在 Jon Skeet 的博客 post More Fun with DateTime, and is also mentioned in the comments in the .NET Framework Reference Source. In practice, you should only encounter this in the ambiguous hour during a fall-back daylight saving time 中被描述为 "DateTime's Deep Dark Secret" 转换(例如 2015 年 11 月 1 日上周日刚刚发生在美国)。

现在,更有可能的情况是您的 dtCutOff 实际上不是来自 DateTime.Now,而是来自用户输入或数据库查找或其他某种机制,那么它可能实际上代表与本地计算机上的时间不同的其他时区的当地时间。换句话说,如果 dtCutOffKindDateTimeKind.Utc,则该值以 UTC 表示。如果它有 DateTimeKind.UnspecifiedKind,那么值 可能 是根据 UTC,或本地时区,或完全是其他时区。

关键是:两个 DateTime 值的比较 评估 Ticks 属性 的基础值。它不考虑 Kind.

由于文件时间是通用时间的绝对点(无论如何在 NTFS 上),那么您确实应该使用 File.GetLastWriteTimeUtc 方法,而不是本地时间有效的方法。

您可以使用两种方法:

  • 加载 modified 属性 作为 UTC,使用:

    myResult.modified = File.GetLastWriteTimeUtc(myFile);
    
  • 适当填充dtOffset

    • 如果您从当前时间加载,则使用 DateTime.UtcNow
    • 如果您从其他输入加载,请确保将值转换为 UTC 以匹配输入场景。例如,如果值是本地时区,则使用 .ToUniversalTime(),如果值是另一个时区,则使用 TimeZoneInfo class 中的转换函数。

  • modified 属性 更改为 DateTimeOffset 而不是 DateTime
  • 加载使用:

    myResult.modified = new DateTimeOffset(File.GetLastWriteTimeUtc(myFile));
    
  • dtCutOff 定义为 DateTimeOffset,并适当填充。

    • 如果您从当前时间加载,则使用 DateTimeOffset.UtcNow
    • 如果您从其他输入加载,请确保设置的偏移量与输入方案相匹配。如果您需要从另一个时区转换,请使用 TimeZoneInfo 函数。

DateTimeOffsetDateTime 有很多优点,例如不违反 SRP。它总是代表一个绝对的时刻。在这种情况下,了解 DateTimeOffset 上的比较运算符始终反映该绝对时刻会有所帮助。 (换句话说,它在进行比较之前在内部调整为 UTC。)