9

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

Salah Akbari
  • 39,330
  • 10
  • 79
  • 109
Amit
  • 1,821
  • 1
  • 17
  • 30
  • what about illiterate over list and write every datetime in console ? – Agent_Orange Mar 08 '18 at 06:56
  • [Check This](http://rextester.com/PNVQ23200) – Agent_Orange Mar 08 '18 at 07:01
  • @Agent_Orange I have edited question, I hope that will help you to get situation better – Amit Mar 08 '18 at 07:02
  • 6
    Hint: Print out the Ticks property of `date` and `dateNow` before and after you modify `dateNow`. See anything unusual? What can we conclude about the resolution of `DateTime`? – Eric Lippert Mar 08 '18 at 07:15
  • @EricLippert yeah, I got that. they are different. Any suggestion most optimized way to eliminate this ticks difference from date which is originally taken from DateTime.Now() method – Amit Mar 08 '18 at 07:19
  • You already know how to do it; your program does it right when initializing `date`. – Eric Lippert Mar 08 '18 at 07:21
  • @EricLippert So, creating new dates with passing 0 in constructor as seconds (eventually takes care about ticks) is the best solution! – Amit Mar 08 '18 at 07:23

5 Answers5

43

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.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
23

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.

Salah Akbari
  • 39,330
  • 10
  • 79
  • 109
10

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.

Sweeper
  • 213,210
  • 22
  • 193
  • 313
  • 2
    Removing the Seconds and Milliseconds from 'dateNow' after initializing and using the DateTime(long ticks) constructor for 'date' should do it – Hyarus Mar 08 '18 at 07:25
2

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.

Shaun Luttin
  • 133,272
  • 81
  • 405
  • 467
0

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");
Salah Akbari
  • 39,330
  • 10
  • 79
  • 109
controlbox
  • 522
  • 4
  • 13