By default C# compares DateTime objects to the 100ns tick. However, my database returns DateTime values to the nearest millisecond. What\'s the best way to compare two DateT
I had a similar problem as the original questioner but to make things more interesting I was saving and retrieving Nullable.
I liked joshperry's answer and extended it to work for my purposes:
public static class DateTimeTolerance
{
private static TimeSpan _defaultTolerance = TimeSpan.FromMilliseconds(10); // 10ms default resolution
public static void SetDefault(TimeSpan tolerance)
{
_defaultTolerance = tolerance;
}
public static DateTimeWithin Within(this DateTime dateTime, TimeSpan tolerance)
{
return new DateTimeWithin(dateTime, tolerance);
}
public static DateTimeWithin Within(this DateTime dateTime)
{
return new DateTimeWithin(dateTime, _defaultTolerance);
}
// Additional overload that can deal with Nullable dates
// (treats null as DateTime.MinValue)
public static DateTimeWithin Within(this DateTime? dateTime)
{
return dateTime.GetValueOrDefault().Within();
}
public static DateTimeWithin Within(this DateTime? dateTime, TimeSpan tolerance)
{
return dateTime.GetValueOrDefault().Within(tolerance);
}
}
public class DateTimeWithin
{
public DateTimeWithin(DateTime dateTime, TimeSpan tolerance)
{
DateTime = dateTime;
Tolerance = tolerance;
}
public TimeSpan Tolerance { get; private set; }
public DateTime DateTime { get; private set; }
public static bool operator ==(DateTime lhs, DateTimeWithin rhs)
{
return (lhs - rhs.DateTime).Duration() <= rhs.Tolerance;
}
public static bool operator !=(DateTime lhs, DateTimeWithin rhs)
{
return (lhs - rhs.DateTime).Duration() > rhs.Tolerance;
}
public static bool operator ==(DateTimeWithin lhs, DateTime rhs)
{
return rhs == lhs;
}
public static bool operator !=(DateTimeWithin lhs, DateTime rhs)
{
return rhs != lhs;
}
// Overloads that can deal with Nullable dates
public static bool operator !=(DateTimeWithin lhs, DateTime? rhs)
{
return rhs != lhs;
}
public static bool operator ==(DateTime? lhs, DateTimeWithin rhs)
{
if (!lhs.HasValue && rhs.DateTime == default(DateTime)) return true;
if (!lhs.HasValue) return false;
return (lhs.Value - rhs.DateTime).Duration() <= rhs.Tolerance;
}
public static bool operator !=(DateTime? lhs, DateTimeWithin rhs)
{
if (!lhs.HasValue && rhs.DateTime == default(DateTime)) return true;
if (!lhs.HasValue) return false;
return (lhs.Value - rhs.DateTime).Duration() > rhs.Tolerance;
}
public static bool operator ==(DateTimeWithin lhs, DateTime? rhs)
{
return rhs == lhs;
}
}
And a quick unit test to verify everything is working correctly:
[TestMethod]
public void DateTimeExtensions_Within_WorksWithNullable()
{
var now = DateTime.Now;
var dtNow1 = new DateTime?(now);
var dtNow2 = new DateTime?(now.AddMilliseconds(1));
var dtNowish = new DateTime?(now.AddMilliseconds(25));
DateTime? dtNull = null;
Assert.IsTrue(now == dtNow1.Within()); // Compare DateTime to DateTime?
Assert.IsTrue(dtNow1 == dtNow2.Within()); // Compare two DateTime? using a different syntax
Assert.IsTrue(dtNow1 == dtNow2.Within()); // Same value should be true
Assert.IsFalse(dtNow1 == dtNowish.Within()); // Outside of the default 10ms tolerance, should not be equal
Assert.IsTrue(dtNow1 == dtNowish.Within(TimeSpan.FromMilliseconds(50))); // ... but we can override this
Assert.IsFalse(dtNow1 == dtNull.Within()); // Comparing a value to null should be false
Assert.IsTrue(dtNull == dtNull.Within()); // ... but two nulls should be true
}