2

I have two dates and I only want to make sure they match on these 6 fields: year, month, day, hour, minute and second.

I have noticed that if I perform a simple equality == comparison if(d1 == d2) that match on these fields, I still get 'false'. I'm assuming this has to do with other fields under the hood that relate to ticks, milliseconds etc. How can I ignore everything and just make sure they match on the 6 fields above?

I have created the prototype function below but to me this feels amateurish and inefficient for production-level code. Furthermore, date1 has to be a nullable datetime.

Does anyone else have any better suggestions?

    private static bool DatesAreEqual(DateTime date1, DateTime date2)
    {

        var d1 = new DateTime(date1.Year, date1.Month, date1.Day,
                    date1.Hour, date1.Minute, date1.Second);

        var d2 = new DateTime(date2.Year, date2.Month, date2.Day,
                    date2.Hour, date2.Minute, date2.Second);

        return d1 == d2;
    }
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
Extranomical
  • 385
  • 1
  • 3
  • 13
  • Why not use an equality check on the members? You might also want to take DateTimeKind into consideration. Example: `return date1.HasValue && date1.Year == date2.Year && date1.Month == date2.Month && .....`; – Igor Feb 28 '22 at 19:06
  • Well, one good soltuion is to ensure that data sent/saved to the database only ever resolves to say date and time to the minute. Or perhpas the second. Often this might not be your code and choice, so what you have is quite much your choice here. But, I tend to make a strong solid choice at program development time - and ONLY allow all datetime values to ever be saved and resolved to the nearest minute (or second). If that was not a choice, or can't be a choice now? Then I can't see much of any other choice other then your function. You might be able to find + change where dateime is created. – Albert D. Kallal Feb 28 '22 at 19:10
  • 1
    Does this answer your question? [DateTime Comparison Precision](https://stackoverflow.com/questions/2956748/datetime-comparison-precision) – Heretic Monkey Feb 28 '22 at 19:22
  • When `new`ing a date explicitly set all properties down to your desired resolution. The other properties default to zero. A direct compare `date1 == date2` compares all properties, but zero equals zero so zeroed milliseconds, for example, is effectively not compared. – radarbob Feb 28 '22 at 19:28
  • 1
    @radarbob: No, `date1 == date2` doesn't compare all properties. It effectively just compares `Ticks`, from which most of the rest of the properties are derived. But crucially, it *doesn't* compare the `Kind` properties. – Jon Skeet Feb 28 '22 at 19:30
  • @Igor Since we know all of the date is being compared, how about `date1.Date == date2.Date && date1.Hour == date2.Hour && date1.Minute == date2.Minute && date1.Second == date2.Second`? – NetMage Feb 28 '22 at 20:12

3 Answers3

1

You can remove fractional part of the dates (please, note, that fractional part is longer then just milliseconds):

DateTime date = DateTime.Now;

// 28 02 2022 22:19:56.3704625 
Console.WriteLine($"{date:dd MM yyyy HH:mm:ss.fffffff}"); 

date -= TimeSpan.FromTicks(date.Ticks % 10_000_000);

// 28 02 2022 22:19:56.0000000
Console.WriteLine($"{date:dd MM yyyy HH:mm:ss.fffffff}");

Code:

DateTime date1 = ...
DateTime date2 = ...

...

if (date1 - TimeSpan.FromTicks(date1.Ticks % 10_000_000) == 
    date2 - TimeSpan.FromTicks(date2.Ticks % 10_000_000)) {
  //TODO: Relevant code here
}

You can implement extension class to keep main code shorter and more readable:

public partial static class DateTimeExtensions {
  public static DateTime TrimToSeconds(this DateTime value) => 
    value - TimeSpan.FromTicks(value.Ticks % 10_000_000)
}

And then

DateTime date1 = ...
DateTime date2 = ...

...

if (date1.TrimToSeconds() == date2.TrimToSeconds()) {
  //TODO: Relevant code here
}
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
1

If you check on the TimeSpam, we have the TimeSpan.TicksPerSecond that is the minimum that you want to be the same (the seconds), so we reset that part to zero and we make the compare using ticks (that is the faster) as:

pubic static bool DatesAreEqual(DateTime d1, DateTime d2)
{
    return (d1.Ticks - (d1.Ticks % TimeSpan.TicksPerSecond)) == (d2.Ticks - (d2.Ticks % TimeSpan.TicksPerSecond));
}

With this way you just make some number compare and its the faster way.

More Than That

Base on the above code I also make two more functions with selected Precision and Equal, or Compare functions

public enum DateTimeComparePrecision : long
{
    Millisecond  = TimeSpan.TicksPerMillisecond,
    Second = TimeSpan.TicksPerSecond,
    Minute = TimeSpan.TicksPerMinute,
    Hour = TimeSpan.TicksPerHour,
    Day = TimeSpan.TicksPerDay,
}


public static bool DatesAreEqual(DateTime d1, DateTime d2, DateTimeComparePrecision Precision)
{
    return (d1.Ticks - (d1.Ticks % (long)Precision)) == (d2.Ticks - (d2.Ticks % (long)Precision));
}        


public static int DatesCompare(DateTime d1, DateTime d2, DateTimeComparePrecision Precision)
{
    long Day1 = (d1.Ticks - (d1.Ticks % (long)Precision));
    long Day2 = (d2.Ticks - (d2.Ticks % (long)Precision));

    if (Day2 > Day1) 
        return 1;            

    if (Day2 < Day1)            
        return -1;            

    return 0;
}

Simple Visual Test

DateTime NowIs = DateTime.UtcNow;
Console.WriteLine($"{NowIs:dd MM yyyy HH:mm:ss.fffffff}");

DateTime d1 = new DateTime((NowIs.Ticks - (NowIs.Ticks % TimeSpan.TicksPerMillisecond)));
Console.WriteLine($"{d1:dd MM yyyy HH:mm:ss.fffffff}");

d1 = new DateTime((NowIs.Ticks - (NowIs.Ticks % TimeSpan.TicksPerSecond)));
Console.WriteLine($"{d1:dd MM yyyy HH:mm:ss.fffffff}");

d1 = new DateTime((NowIs.Ticks - (NowIs.Ticks % TimeSpan.TicksPerMinute)));
Console.WriteLine($"{d1:dd MM yyyy HH:mm:ss.fffffff}");

d1 = new DateTime((NowIs.Ticks - (NowIs.Ticks % TimeSpan.TicksPerHour)));
Console.WriteLine($"{d1:dd MM yyyy HH:mm:ss.fffffff}");

d1 = new DateTime((NowIs.Ticks - (NowIs.Ticks % TimeSpan.TicksPerDay)));
Console.WriteLine($"{d1:dd MM yyyy HH:mm:ss.fffffff}");

output

01 03 2022 12:51:26.7237323
01 03 2022 12:51:26.7230000
01 03 2022 12:51:26.0000000
01 03 2022 12:51:00.0000000
01 03 2022 12:00:00.0000000
01 03 2022 00:00:00.0000000
Aristos
  • 66,005
  • 16
  • 114
  • 150
-1

This might be slightly quicker:

var sameDateTime = ((date1 - date2).Milliseconds < 1000);

https://stackoverflow.com/a/58173628/759558

Neil
  • 11,059
  • 3
  • 31
  • 56
  • 1
    What if the dates are within 1000 milliseconds on either side of midnight? That doesn't fit OP's functional requirement. – madreflection Feb 28 '22 at 19:16
  • Yup. good point. – Neil Feb 28 '22 at 19:16
  • 1
    They don't even need to be on either side of midnight - just either side of a second boundary, e.g. 2022-02-28T19:24:05.800 and 2022-02-28T19:24:06.200. Those are less than a second apart, but don't meet the OP's requirements. – Jon Skeet Feb 28 '22 at 19:25
  • @JonSkeet: Of course, you're absolutely right. I was thinking about whole dates (year to day), for some reason. – madreflection Feb 28 '22 at 19:35