2

I'm trying to compare two DateTimeOffsets but the DateTimeOffset.Compare() function is not functioning as expected. I have created the example script to demonstrate the issue. In this example I expected the result of comparing dateA and dateB to be zero (The same).

using System;
namespace ComparingDateTimeOffset
{
   class Program
   {
       static void Main(string[] args)
       {
           DateTimeOffset dateA = DateTimeOffset.Now;
           Thread.Sleep(1);
           DateTimeOffset dateB = DateTimeOffset.Now;

           Console.WriteLine("dateA =" + dateA);
           Console.WriteLine("dateB =" + dateB);

           Console.WriteLine(DateTimeOffset.Compare(dateA, dateB) == 0
            ? "dateA and dateB are the same"
            : "dateA and dateB are NOT the same");

           Console.WriteLine("Press any key to exit");
           Console.ReadKey();
       }
   }
}

The results of the above program are

dateA =17/02/2016 09:09:21 +00:00
dateB =17/02/2016 09:09:21 +00:00
dateA and dateB are NOT the same
Press any key to exit

In the console output it looks like the two dates are equal. But the compare function says the two dates are different. The following code shows that there are a few milliseconds difference between the two dates.

long diff = (long) (dateB - dateA).TotalMilliseconds;
Console.WriteLine("Time differance in milliseconds =" + diff);

To avoid using the DateTimeOffset.Compare function. I have decided to calculate the difference between the dates in seconds and then round to the nearest integer. This seams to work. Can anyone see a disadvantage of using this method?

Console.WriteLine((int)(dateB - dateA).TotalSeconds == 0
            ? "dateA and dateB are the same"
            : "dateA and dateB are NOT the same");
Andrew Seaford
  • 645
  • 6
  • 13
  • 2
    You're not rounding to the *nearest* integer - you're truncating towards zero. So if the difference between them is 0.99 seconds, that's still 0. If you're happy with that, that's fine. Another alternative would be to round or truncate both DateTimeOffset values to "second precision" and then compare them. It's also not clear why you're using Compare rather than just checking with `==` anyway. – Jon Skeet Feb 17 '16 at 09:56
  • 3
    Why is the compare function not operating as expected? It sounds like it is operating exactly as expected. Your values are not the same and the function tells you they are not. – Lukos Feb 17 '16 at 09:57
  • I see what you doing but in such a case they will not be the _same_. They are same parts of date, hour, minute and second but this is not _real_ equality for `DateTimeOffset`. – Soner Gönül Feb 17 '16 at 09:57
  • 1
    And the question is? They clearly aren't exactly equal. And if you want to compare for equality, there are the `Equals` and the `EqualsExact` methods, instead of the `Compare` that is normally used to find greater/smaller. – xanatos Feb 17 '16 at 09:57
  • 1
    The question is how do I compare two datetimeoffset's ignoring the milliseconds? – Andrew Seaford Feb 17 '16 at 13:13

3 Answers3

2

First of all, DateTimeOffset and DateTime have finer precision than milliseconds. The smallest unit they represent is a "tick" which is 100ns. In other words, there are seven decimal places of fractional seconds, not three. (The computer's clock itself is not that precise, but these data structures can still represent values with this precision.)

Ultimately, you asked (in the question comments):

... how do I compare two datetimeoffset's ignoring the milliseconds?

So know we know what you're asking, consider that just ignoring fractional seconds will not solve the problem.

  • Consider if A had 3.9999999 seconds, and B had 4.0000001.
    Truncating decimals would give A == 3, B == 4, so A != B.

  • Consider if A had 3.4999999 seconds, and B had 3.5000001.
    Rounding decimals would give A == 3, B == 4, so A != B.

So neither truncation or rounding will solve all use cases. You cannot guarantee that the value between two consecutive clock readings will be truncated or rounded to the same value.

Instead, as you suggested, decide what the smallest difference between two values you will allow to consider them the same. For example, if you think anything under one second apart is inconsequential, then:

if ((dateB - dateA) < TimeSpan.FromSeconds(1))
{
    // equivalent, within 1s threshold
}
else
{
    // not equivalent or within threshold.
    // you could continue with normal comparison to determine < or > if desired
}

Your implementation of: (int)(dateB - dateA).TotalSeconds == 0 is pretty much the same thing as this, so yes - that approach is fine. Though you may prefer my implementation for readability, and for better control over the threshold value, should you decide to change it to something else.

Matt Johnson-Pint
  • 230,703
  • 74
  • 448
  • 575
0

Your Timespans are not the same, as can be seen if you use an appropriate format specifier, e.g. round trip format "o".

dateA.ToString("o")

Here are some examples on how to truncate the milliseconds.

Community
  • 1
  • 1
Georg Patscheider
  • 9,357
  • 1
  • 26
  • 36
0

Sleep(1) will only sleep for 1 millisecond. So presumably dateA and dateB are only 1 or 2 milliseconds apart.

Since you are printing the date/time value only to minutes and seconds, you do not see the actual difference. If you were to use the Console.WriteLine("dateA =" + dateA.ToString("dd/MM/yyyy HH:mm:ss.fff"); you'd see the difference.

If you meant to wait for 1 second, you should do a Sleep(1000)

Jens Meinecke
  • 2,904
  • 17
  • 20