Any() method on List doesn't work as

2020-07-08 07:19发布

I m working on .net 4.6 in winforms (here code is from test console application)

At one point I'm having a list of DateTime and I need to figure out if this list contains specific date or not.

For that I m trying use Any() on the list. Even if the list does contain the desired date, Any() returns false only.

Following is example code, which also have same behavior. So if I can get any idea on this code, I guess it will help on my real code too.

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");

output:

enter image description here

5条回答
萌系小妹纸
2楼-- · 2020-07-08 07:54

Problem

Your synchronization with AddSeconds and AddMilliseconds works to the precision of fff (milliseconds) but not to the precision of Ticks (one ten-millionth of a second). The latter is required for the DateTime equality that Any() uses.

One Solution

Precisely sync the DateTime copy with its prototype by creating that copy with the DateTime constructor that takes Ticks. Then your code accurately finds the date with Any().

Here 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");

Aside

We can format a date string to the precision of ticks by using fffffff instead of fff.

查看更多
一夜七次
3楼-- · 2020-07-08 07:58

The Ticks and TimeOfDay properties of items in your dateTimeList is not equal to Ticks and TimeOfDay properties of your dateNow, and dateNow has more ticks than the one in your dateTimeList. You need to add this line:

dateNow = new DateTime(dateNow.Year, dateNow.Month,
           dateNow.Day, dateNow.Hour, dateNow.Minute, 00);

This will make the Ticks and TimeOfDay properties of your dateNow equals to ones that you've added to your dateTimeList.

查看更多
淡お忘
4楼-- · 2020-07-08 07:58

There's a saying in computer programming "select isn't broken". It means that when some basic, commonly used, heavily tested bit of software seems to be broken, the problem is that you've misdiagnosed the problem, not that the tool is broken.

Any works just fine.

The mistake is that you are rounding the date correctly in one place and incorrectly in the other, and the incorrectly rounded date is not equal to the correctly rounded date. Use the Ticks property on the dates to see why one of your rounding techniques is good and one of them is totally wrong.

查看更多
家丑人穷心不美
5楼-- · 2020-07-08 08:05

The key to finding out why this happens is finding out what's the difference between the two DateTimes that we are comparing.

If you print out the Ticks property of the date times, you'll find something like this:

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

As you can see, these two lines are likely to be the two DateTimes that you think would equal, but does not:

636560893800004640
636560893800000000

The one above is the dateNow and the one below is the one in the list.

See the difference? dateNow has more ticks than the one in the list.

Why is this?

The DateTimes in the list are created from date, which is created by using the constructor with 6 arguments. This creates a DateTime just as you specified. This means that the instance created will not have any extra ticks for the "remainder". And I can see that when you change your dateNow, you tried to remove all the extra components that you don't care about, like seconds and milliseconds, but you forgot about ticks. When you compare 2 DateTimes you are actually comparing the ticks.

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

So you need to remove the extra ticks from your dateNow to get your desired result, or just use the 6-argument constructor again.

查看更多
再贱就再见
6楼-- · 2020-07-08 08:20

DateTime uses the System Clock which is notoriously only accurate to about 10-15ms - as highlighted in the answer to this question - Get DateTime.Now with milliseconds precision

To get round this, you'll need to replace your equality comparison (==) in your Any() clause with a check that takes account of the inaccuracy. The code below matches the dates if they are less than 20ms apart...

if (dateTimeList.Any(x => Math.Abs((dateNow - x).TotalMilliseconds) < 20)
    Console.WriteLine("date found");
else
    Console.WriteLine("date Not found");
查看更多
登录 后发表回答