【问题标题】:System.DateTime comparison ('>') or ('<') does not give what I expectedSystem.DateTime 比较 ('>') 或 ('<') 没有达到我的预期
【发布时间】:2015-11-05 17:45:24
【问题描述】:

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

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

我有一个 LastWriteTime 为“{11/3/2015 1:33:26 PM}”的文件被我的收藏使用 dtCutOff ==“{11/3/2015 1:33:26 PM}”拾取!所以 '>' 似乎不起作用。

【问题讨论】:

  • 请展示完整的代码和示例。我们不知道您如何定义 fn_fileInfodtCutOff,我们不知道 dtCutOff 中的值或您要比较的文件日期。
  • 这没有帮助。 fn_fileInfo 不是.NET 的标准功能,据我所知,它也不是由LinqPad 提供的。您需要将您的问题编辑为MVCE。否则它将被投票为关闭。我对这里可能发生的事情有些怀疑,但我无法在问题的当前状态下回答,因为这样的答案将是高度推测性的。
  • 一些我在一段时间内见过的最糟糕的 C# 代码......外壳,不必要的结构......呃......
  • 亲爱的@JennaLeaf,你完全没有抓住重点。我告诉你的是,你遇到的问题与 LINQ 或 lambdas 无关
  • 实际上,我们可以利用我们的时间做其他事情,但我们大多数人都在努力帮助您。

标签: datetime operators


【解决方案1】:

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

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

对于DateTime.NowKind 将是DateTimeKind.LocalFile.GetLastWriteTime 也以本地类型返回其值。因此,如果您始终按照问题中显示的方式从 DateTime.Now 导出您的 dtCutOff,那么它几乎始终是正确的比较函数。

“几乎”源于DateTimeKind.Local 实际上可以代表两种不同的类型。换句话说,实际上有四种种,但其中两种被一种暴露出来。这在 Jon Skeet 的博文 More Fun with DateTime 中被描述为“DateTime 的深层秘密”,也提到了 in the comments in the .NET Framework Reference Source。在实践中,您应该只在回退daylight saving time 转换期间的模棱两可的时间遇到​​这种情况(例如刚刚发生在美国的上周日 2015 年 11 月 1 日)。

现在,更可能的情况是您的dtCutOff 实际上不是来自DateTime.Now,而是来自用户输入或数据库查找或其他一些机制,那么它可能实际上代表其他一些地方的本地时间时区比您本地计算机上的时区。换句话说,如果dtCutOffKindDateTimeKind.Utc,则该值以UTC 表示。如果它的 KindDateTimeKind.Unspecified,则值可能是 UTC、本地时区或其他时区。

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

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

您可以使用两种方法:

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

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

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

  • 将您的 modified 属性更改为 DateTimeOffset 而不是 DateTime
  • 使用以下方式加载:

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

    • 如果您从当前时间加载,请使用DateTimeOffset.UtcNow
    • 如果您从其他输入加载,请确保设置偏移量以匹配输入场景。如果您需要从其他时区转换,请使用 TimeZoneInfo 函数。

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

【讨论】:

    【解决方案2】:

    此代码有效:

    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),以确保这不是一些奇怪的时区问题

    【讨论】:

    • 抱歉 Sten 你的版本不适合我!我有一个文件,其修改日期为 FileInfo 中的“{11/3/2015 1:33:26 PM}”,当我的 cutOffDate 为“{11/3/2015 1:33:26 PM}”时,它直接进入了我的收藏夹}”!!!正如我在上面的评论中给出的那样,我必须回到我自己的解决方案,对不起!
    • @JennaLeaf - 听起来像是一个精度问题。 DateTime 和 NTFS 文件时间都有 7 个小数位(精度为 100-ns)。您只看到整秒,但这并不意味着基础值不会大几分之一秒。
    【解决方案3】:

    由于文件每天被送入队列一次,因此不需要精确到毫秒或其他时间。因此,可以接受一秒钟的 TimeSpan 差异来解决问题并让我的案例发挥作用。

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

    现在,当我的 cutOffDate 为“{11/3/2015 1:33:26 PM}”时,我的修改日期为“{11/3/2015 1:33:26 PM}”的文件没有进入我的收藏夹而我的另一个修改日期“{11/3/2015 1:33:27 PM}”的文件已按预期成功传递到我的收藏中!所以,它有效,这就是在所有这些建议之后它应该如何工作!谢谢大家。

    【讨论】:

    • Matt:我已经更正了我的答案,请编辑或调整您的评价,谢谢。
    • 我认为您正在查看的文件时间实际上可能比您想象的要长,只有几分之一秒。因此,通过减去一秒,您会得到您所期望的响应,但由于实际时间实际上更大,所以它有点虚伪。这只是您在场景中关心多少精度的问题。 (有些可能会走得更远,比如一整分钟)
    【解决方案4】:

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

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

    【讨论】:

    • Directory.GetFiles 返回string[],所以file 是一个字符串,不会有modified 属性。
    猜你喜欢
    • 2021-10-15
    • 2016-09-05
    • 1970-01-01
    • 1970-01-01
    • 2019-11-27
    • 2012-05-17
    • 1970-01-01
    • 2020-09-23
    • 1970-01-01
    相关资源
    最近更新 更多