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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/381401/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 01:20:50  来源:igfitidea点击:

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

c#datetimecomparisonresolution

提问by dthrasher

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 DateTime objects in C# using a specified tolerance?

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

Edit: I'm dealing with a truncation issue, not a rounding issue. As Joe points out below, a rounding issue would introduce new questions.

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

The solution that works for me is a combination of those below.

对我有用的解决方案是以下解决方案的组合。

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

This returns true if the difference is less than one millisecond. The call to Duration() is important in order to get the absolute value of the difference between the two dates.

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

采纳答案by Micah

I usally use the TimeSpan.FromXXX methods to do something like this:

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

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

回答by JoshBerke

You need to remove the milliseconds component from the date object. One way is:

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

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

You can also subtract two datetimes

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

d.Subtract(DateTime.Now);

d.Subtract(DateTime.Now);

This will return a timespan object which you can use to compare the days, hours, minutes and seconds components to see the difference.

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

回答by Charles Bretana

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

回答by joshperry

How about an extension method for DateTime to make a bit of a fluent interface (those are all the rage right?)

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

This relies on a class to store the state and define a couple operator overloads for == and != :

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

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

Then in your code you can do:

然后在您的代码中,您可以执行以下操作:

DateTime d1 = DateTime.Now;
DateTime d2 = d1 + TimeSpan.FromSeconds(20);

if(d1 == d2.Within(TimeSpan.FromMinutes(1))) {
    // TRUE! Do whatever
}

The extension class also houses a default static tolerance so that you can set a tolerance for your whole project and use the Within method with no parameters:

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

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

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

I have a few unit tests but that'd be a bit too much code for pasting here.

我有一些单元测试,但粘贴在这里的代码有点太多了。

回答by Joe

By default C# compares DateTime objects to the millesecond.

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

Actually the resolution is to the 100ns tick.

实际上分辨率是 100ns 滴答。

If you're comparing two DateTime values from the database, which have 1s resolution, no problem.

如果您要比较来自数据库的两个 DateTime 值(分辨率为 1 秒),没问题。

If you're comparing with a DateTime from another source (e.g. the current DateTime using DateTime.Now)), then you need to decide how you want the fractions of a second to be treated. E.g. rounded to nearest or truncated? How to round if it's exactly half a second.

如果您要与来自其他来源的 DateTime(例如,使用 DateTime.Now 的当前 DateTime)进行比较,那么您需要决定如何处理秒的分数。例如,四舍五入到最接近的还是截断的?如果正好是半秒,如何舍入。

I suggest you round or truncate to an integral number of seconds, then compare with the value from the database. Here's a post that describes how to round a DateTime(this example rounds to minutes, but the principal is the same).

我建议您四舍五入或截断到整数秒,然后与数据库中的值进行比较。 这是一篇描述如何舍入日期时间的帖子(此示例舍入到分钟,但主要内容相同)。

回答by Nate Barbettini

I had a similar problem as the original questioner but to make things more interesting I was saving and retrieving Nullable<DateTime>.

我遇到了与原始提问者类似的问题,但为了让事情更有趣,我正在保存和检索Nullable<DateTime>.

I liked joshperry's answerand extended it to work for my purposes:

我喜欢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;
    }
}

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
}

回答by Michael Freidgeim

I've created extension methods IsSimilar

我创建了扩展方法 IsSimilar

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