如何在C#中使用指定的容差比较DateTime对象?

默认情况下,C#将DateTime对象与100ns刻度进行比较。 但是,我的数据库将DateTime值返回到最接近的毫秒。 使用指定的容差比较C#中两个DateTime对象的最佳方法是什么?

编辑:我正在处理截断问题,而不是舍入问题。 正如Joe在下面指出的那样,舍入问题将引入新的问题。

适合我的解决方案是以下组合。

(dateTime1 - dateTime2).Duration() < TimeSpan.FromMilliseconds(1) 

如果差值小于1毫秒,则返回true。 对Duration()的调用很重要,以获得两个日期之间差异的绝对值。

我通常使用TimeSpan.FromXXX方法做这样的事情:

 if((myDate - myOtherDate) > TimeSpan.FromSeconds(10)) { //Do something here } 

如何使用DateTime的扩展方法来创建一个流畅的界面(那些风靡一时吧?)

 public static class DateTimeTolerance { private static TimeSpan _defaultTolerance = TimeSpan.FromSeconds(10); 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); } } 

这依赖于一个类来存储状态并为==和!=定义一对运算符重载:

 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; } } 

然后在您的代码中,您可以:

 DateTime d1 = DateTime.Now; DateTime d2 = d1 + TimeSpan.FromSeconds(20); if(d1 == d2.Within(TimeSpan.FromMinutes(1))) { // TRUE! Do whatever } 

扩展类还包含默认的静态容差,以便您可以为整个项目设置容差,并使用不带参数的Within方法:

 DateTimeTolerance.SetDefault(TimeSpan.FromMinutes(1)); if(d1 == d2.Within()) { // Uses default tolerance // TRUE! Do whatever } 

我有一些unit testing但是这里的代码有点太多了。

您需要从日期对象中删除毫秒组件。 一种方法是:

  DateTime d = DateTime.Now; d.Subtract(new TimeSpan(0, 0, 0, 0, d.Millisecond)); 

您还可以减去两个日期时间

d.Subtract(DateTime.Now);

这将返回一个timepan对象,您可以使用它来比较日,小时,分钟和秒组件以查看差异。

  if (Math.Abs(dt1.Subtract(dt2).TotalSeconds) < 1.0) 

默认情况下,C#将DateTime对象与millesecond进行比较。

实际上,分辨率为100ns。

如果您正在比较数据库中两个具有1分辨率的DateTime值,则没有问题。

如果您正在与另一个源的DateTime进行比较(例如,使用DateTime.Now的当前DateTime),那么您需要决定如何处理一秒的分数。 例如舍入到最近或截断? 如果它恰好是半秒钟,如何圆。

我建议你舍入或截断为整数秒,然后与数据库中的值进行比较。 这是一篇描述如何舍入DateTime的post (此示例舍入为分钟,但主体是相同的)。

我遇到了与原始提问者类似的问题,但为了让事情变得更有趣,我保存并检索Nullable

我喜欢joshperry的回答并将其扩展为我的目的:

 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; } } 

并进行快速unit testing以validation一切正常:

 [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 }