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

问题描述 投票:32回答:7

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

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

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

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

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

c# datetime comparison resolution
7个回答
27
投票
if((myDate - myOtherDate) > TimeSpan.FromSeconds(10)) { //Do something here }

10
投票
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
}

扩展类还具有默认的静态公差,因此您可以为整个项目设置公差,并使用不带参数的Inside方法:

DateTimeTolerance.SetDefault(TimeSpan.FromMinutes(1));

if(d1 == d2.Within()) {  // Uses default tolerance
    // TRUE! Do whatever
}

我有一些单元测试,但是要粘贴到这里有点太多代码。


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

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

d.Subtract(DateTime.Now);

这将返回一个时间跨度对象,可用于比较日期,小时,分钟和秒的组成部分以查看差异。


2
投票

1
投票
默认情况下,C#将DateTime对象与毫秒进行比较。
实际上分辨率是100ns刻度。

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

如果要与其他来源的DateTime(例如,使用DateTime.Now的当前DateTime)进行比较,则需要决定如何处理一秒的小数。例如。四舍五入到最接近或截断?如果正好是半秒,该如何取整。

我建议您舍入或截断至整数秒,然后与数据库中的值进行比较。 Here's a post that describes how to round a DateTime(此示例四舍五入为分钟,但原理相同)。


1
投票
我喜欢joshperry's answer,并将其扩展以用于我的目的:

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

以及进行快速的单元测试以验证所有功能是否正常:

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

0
投票
public static bool IsSimilar(this DateTime? lhs, DateTime? rhs, TimeSpan tolerance) { if (!lhs.HasValue && !lhs.HasValue) return true;//both are null if (!lhs.HasValue || !lhs.HasValue) return false;//one of 2 is null return IsSimilar(lhs.Value, rhs.Value, tolerance); } public static bool IsSimilar(this DateTime lhs, DateTime rhs, TimeSpan tolerance) { return (lhs - rhs).Duration() <= tolerance; }
© www.soinside.com 2019 - 2024. All rights reserved.