How do you compare DateTime objects using a specified tolerance in C#?

前端 未结 7 869
清酒与你
清酒与你 2020-12-29 20:07

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

7条回答
  •  谎友^
    谎友^ (楼主)
    2020-12-29 20:44

    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
    }
    

提交回复
热议问题