6

I have two datetimes, one from a timestamp, and another I'm generating in code. I need to test their equality, and would love to do it without too many expressions. Here's an example of my two dates:

DateTime expireTimeStampUTC = 
    DateTime.Parse(UTCValueFromDatabase));
DateTime expectedExpireTime = 
    DateTime.UtcNow.AddHours(NumberOfExpectedHoursInConfig);

This is too high precision of a test:

if (expireTimeStampUTC.Equals(expectedExpireTime)){}

I don't care if they're exact to the second, just the hour.

Could it possibly be the best solution to do something compound like this:

if (expireTimeStampUTC.Date.Equals(expectedExpireTime.Date))
{
    if (!expireTimeStampUTC.Hour.Equals(expectedExpireTime.Hour))
    {
        pass = false;
    }
}

I'm not the most experienced with C#... is there some elegent way to do this?

danieltalsky
  • 7,752
  • 5
  • 39
  • 60

5 Answers5

12

If the problem you are having is because of them is a database type, you could convert to that type and compare there, we lose/gain about a millisecond converting to SQLDateTimes on roughly 1/3 of DB saves.

If not, compare the unit you actually care about:

DateTime dt1 = DateTime.UtcNow;
DateTime dt2 = DateTime.UtcNow.AddMinutes(59); // or 1 or 61 for test values;

// if the dates are in the same hour (12:10 == 12:50, 1:58 != 2:02)
if(dt1.Hour == dt2.Hour) // result

or if you care that they are within an hour time span

// if the dates are within one hour of each other (1:58 == 2:02, 3:30 != 4:45)
if((dt1 - dt2).Duration() < TimeSpan.FromHours(1)) // result

Here subtracting dates generates a time span, the duration is the 'absolute value' and then we create the limit explicitly from a unit we care about (FromHours) and compare.

The last line is as clean as I can think to do equality within a particular time span.

Colin Dabritz
  • 861
  • 8
  • 19
  • Actually I think this is actually better style. I think it's clearer what's happening. I've never changed the "right answer" token before, but I think in this case, this is the better solution. – danieltalsky Mar 05 '09 at 00:34
  • Fair enough. I think it's six one/half a dozen the other. The "Duration()" syntax might be more readable, but it has the (admittedly negligible) overhead of newing up a TimeSpan on both sides of the comparison. – Matt Hamilton Mar 05 '09 at 01:02
6

How about finding the difference between the two hours, and seeing if it's below a certain threshold (say 3600 seconds for an hour)?

var diff = expireTimeStamp.Subtract(expectedExpireTime).TotalSeconds;
pass = Math.Abs(diff) < 3600;
Matt Hamilton
  • 200,371
  • 61
  • 386
  • 320
  • Excellent... that was the answer so stupidly obvious I would kick myself for not thinking of it. Thanks. And with a code sample too. Magical. – danieltalsky Mar 05 '09 at 00:02
  • 1
    Maybe not in *your* C#! Time to upgrade to Visual Studio 2008, mate! :) – Matt Hamilton Mar 05 '09 at 00:04
  • Wild. I think my dev lead would shoot me if I started using that. – danieltalsky Mar 05 '09 at 00:32
  • Nothing harmful about "var". In my code snippet above it's totally obvious what the "diff" variable means, and you don't care about its compile-time type. (Is it Double? Int? Long? Who cares?) – Matt Hamilton Mar 05 '09 at 01:04
4

Subtract them. Check whether the resulting TimeSpan is within a certain maximum range.

recursive
  • 83,943
  • 34
  • 151
  • 241
1

Construct new DateTime objects and compare them. In C#, there's very little penalty for constructing "throwaway" objects in this way.

overslacked
  • 4,127
  • 24
  • 28
1

I experienced the problem when trying to write unit tests which committed then retrieved business objects. I was trying to use the "StartTime" property of my object to ensure the object could be retrieved and encountered this problem. The "StartTime" value committed in the database lost 6 digits of precision in the Ticks value!

Here's how I rewrote my test condition so my test would perform correctly and pass. Relevant line is second-to-last line of block.

DateTime start = DateTime.Now;
NewEventInfo testEvent1 = FakeEvent("test01action", start); //plus other params for testing
mServiceClient.AddEvent(testEvent1);

EventInfo[] eventInfos = null; //code to get back events within time window

Assert.IsNotEmpty(eventInfos);
Assert.GreaterOrEqual(eventInfos.Length, 1);

EventInfo resultEvent1 = eventInfos.FirstOrDefault(e => 
e.Action == "test01action" &&
Math.Abs(e.StartTime.Subtract(testEvent1.StartTime).TotalMilliseconds) < 1000); //checks dates are within 1 sec
Assert.IsNotNull(resultEvent1);

This way I could be sure the object was the one committed by the unit test, as the EventInfo.StartTime property only uses date times with precision of 1 second.

EDIT: Added Math.Abs(around DateTime diff) to ensure absolute value being compared to 1000.

Lisa
  • 4,333
  • 2
  • 27
  • 34