List <datetime>上的Any()方法不按预期工作

时间:2018-03-08 06:54:01

标签: c# .net linq datetime

我在winforms中使用.net 4.6(此处代码来自测试控制台应用程序)

我有一个DateTime列表,我需要弄清楚这个列表是否包含特定日期。

为此,我尝试在列表中使用Any()。 即使列表确实包含所需日期,Any()也只会返回false

以下是示例代码,它也具有相同的行为。所以如果我能对这段代码有所了解,我想这对我的真实代码也有帮助。

List<DateTime> dateTimeList = new List<DateTime>();
DateTime dateNow = DateTime.Now;

DateTime date = new DateTime(dateNow.Year, dateNow.Month, dateNow.Day, dateNow.Hour, dateNow.Minute, 00);
date = date.AddMinutes(-10);
while (date < dateNow.AddMinutes(10))
{
    dateTimeList.Add(date);
    date = date.AddMinutes(1);
}

dateNow = dateNow.AddSeconds(-dateNow.Second);
dateNow = dateNow.AddMilliseconds(-dateNow.Millisecond);

foreach (DateTime dateInList in dateTimeList)
    Console.WriteLine("date list List:" + dateInList.ToString("ddMMyyyy hh:mm:ss:fffz") + " - VS - desired date:" + dateNow.ToString("ddMMyyyy hh:mm:ss:fffz"));

if (dateTimeList.Any(x => x == dateNow))
    Console.WriteLine("date found");
else
    Console.WriteLine("date Not found");

if (dateTimeList.Any(x => x.ToString("ddMMyyyy hh:mm:ss:fffz") == dateNow.ToString("ddMMyyyy hh:mm:ss:fffz")))
    Console.WriteLine("date string matched");
else
    Console.WriteLine("date string didn't match");

输出:

enter image description here

5 个答案:

答案 0 :(得分:43)

在计算机编程中有一句谚语&#34;选择没有被打破&#34;。这意味着当一些基本的,常用的,经过严格测试的软件似乎被打破时,问题在于你误解了问题,而不是工具被破坏了。

Any效果很好。

错误在于您在一个地方正确地舍入日期而在另一个地方错误地舍入日期,并且错误的舍入日期不等于正确舍入的日期。使用日期上的Ticks属性来了解为什么您的一种舍入技术是好的,其中一种是完全错误的。

答案 1 :(得分:23)

dateTimeList中项目的TicksTimeOfDay属性不等于Ticks的{​​{1}}和TimeOfDay属性,以及dateNow的刻度比dateNow中的刻度更多。您需要添加以下行:

dateTimeList

这会使dateNow = new DateTime(dateNow.Year, dateNow.Month, dateNow.Day, dateNow.Hour, dateNow.Minute, 00); 的{​​{1}}和Ticks属性等同于您添加到TimeOfDay的属性。

答案 2 :(得分:10)

找出发生这种情况的原因的关键是找出我们正在比较的两个DateTime之间的区别。

如果您打印出日期时间的Ticks属性,您会发现以下内容:

636560893800004640
636560887800000000
636560893800004640
636560888400000000
636560893800004640
636560889000000000
636560893800004640
636560889600000000
636560893800004640
636560890200000000
636560893800004640
636560890800000000
636560893800004640
636560891400000000
636560893800004640
636560892000000000
636560893800004640
636560892600000000
636560893800004640
636560893200000000
636560893800004640
636560893800000000
636560893800004640
636560894400000000
636560893800004640
636560895000000000
636560893800004640
636560895600000000
636560893800004640
636560896200000000
636560893800004640
636560896800000000
636560893800004640
636560897400000000
636560893800004640
636560898000000000
636560893800004640
636560898600000000
636560893800004640
636560899200000000
636560893800004640
636560899800000000

正如您所看到的,这两行可能是您认为相等的两个DateTime,但不是:

636560893800004640
636560893800000000

上面的那个是dateNow,下面的那个是列表中的那个。

看到区别? dateNow的刻度比列表中的刻度更多。

为什么会这样?

列表中的DateTime是从date创建的,它是使用带有6个参数的构造函数创建的。这会像您指定的那样创建DateTime。这意味着创建的实例不会为“余数”添加任何额外的刻度。我可以看到,当您更改dateNow时,您尝试删除所有不关心的额外组件,例如秒和毫秒,但您忘记了 ticks 。当你比较2 DateTime时,你实际上是在比较滴答。

new DateTime(1) == new DateTime(1)
true
new DateTime(1) == new DateTime(2)
false

因此,您需要从dateNow中删除额外的刻度以获得所需的结果,或者再次使用6参数构造函数。

答案 3 :(得分:2)

问题

AddSecondsAddMilliseconds的同步工作精度为fff(毫秒),但精度为Ticks(千分之一秒) 。后者是Any()使用的DateTime equality所必需的。

一种解决方案

通过使用DateTime constructor that takes Ticks创建该副本,将DateTime副本与其原型精确同步。然后,您的代码会使用Any()准确地找到日期。

此处is your improved code as a working Fiddle

List<DateTime> dateTimeList = new List<DateTime>();
DateTime dateNow = DateTime.Now;

// use dateNow.Ticks in the constructor to create a precise, 
// synchronized DateTime clone
DateTime date = new DateTime(dateNow.Ticks);

date = date.AddMinutes(-10);
while (date < dateNow.AddMinutes(10))
{
    dateTimeList.Add(date);
    date = date.AddMinutes(1);
}

if (dateTimeList.Any(x => x == dateNow))
    Console.WriteLine("date found");
else
    Console.WriteLine("date Not found");

var format = "ddMMyyyy hh:mm:ss:fffz";
if (dateTimeList.Any(x => x.ToString(format) == dateNow.ToString(format)))
    Console.WriteLine("date string matched");
else
    Console.WriteLine("date string didn't match");

除了

我们可以使用fffffff代替fff将日期字符串格式化为刻度的精度。

答案 4 :(得分:0)

日期时间使用系统时钟,众所周知只有 10-15ms - 正如此问题的答案中所强调的那样 - { {3}}

为了解决这个问题,您需要将==子句中的相等比较(Any())替换为考虑到不准确性的检查。下面的代码与日期相匹配,如果它们相隔不到20毫秒......

if (dateTimeList.Any(x => Math.Abs((dateNow - x).TotalMilliseconds) < 20)
    Console.WriteLine("date found");
else
    Console.WriteLine("date Not found");