Как вы сравниваете объекты DateTime, используя указанный допуск в C #? - PullRequest
28 голосов
/ 19 декабря 2008

По умолчанию C # сравнивает объекты DateTime с отметкой 100 нс. Однако моя база данных возвращает значения DateTime с точностью до миллисекунды. Каков наилучший способ сравнения двух объектов DateTime в C # с использованием указанного допуска?

Редактировать: я имею дело с проблемой усечения, а не с проблемой округления. Как Джо указывает ниже, проблема округления привела бы к новым вопросам.

Решение, которое работает для меня, представляет собой комбинацию из приведенных ниже.

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

Возвращает true, если разница меньше одной миллисекунды. Вызов Duration () важен для получения абсолютного значения разницы между двумя датами.

Ответы [ 6 ]

25 голосов
/ 19 декабря 2008

Я обычно использую методы TimeSpan.FromXXX, чтобы сделать что-то вроде этого:

if((myDate - myOtherDate) > TimeSpan.FromSeconds(10))
{
   //Do something here
}
10 голосов
/ 19 декабря 2008

Как насчет метода расширения для 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
}

У меня есть несколько модульных тестов, но для вставки здесь было бы слишком много кода.

5 голосов
/ 19 декабря 2008

Вам необходимо удалить компонент миллисекунд из объекта даты. Один из способов:

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

Вы также можете вычесть две даты

d.Subtract (DateTime.Now);

Это вернет объект временной шкалы, который вы можете использовать для сравнения компонентов дней, часов, минут и секунд, чтобы увидеть разницу.

2 голосов
/ 19 декабря 2008
        if (Math.Abs(dt1.Subtract(dt2).TotalSeconds) < 1.0)
1 голос
/ 08 декабря 2014

У меня была та же проблема, что и у исходного спрашивающего, но чтобы сделать вещи более интересными, я сохранял и извлекал Nullable<DateTime>.

Мне понравился ответ Джошперри и расширил его, чтобы он работал для моих целей:

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
}
1 голос
/ 19 декабря 2008

По умолчанию C # сравнивает объекты DateTime с миллисекундами.

На самом деле разрешение составляет 100 нс.

Если вы сравниваете два значения DateTime из базы данных с разрешением 1 с, проблем не возникает.

Если вы сравниваете DateTime из другого источника (например, текущий DateTime с использованием DateTime.Now)), вам нужно решить, как вы хотите обрабатывать доли секунды. Например. округлено до ближайшего или усечено? Как округлить, если это ровно полсекунды.

Я предлагаю вам округлить или усечь до целого числа секунд, а затем сравнить со значением из базы данных. Вот пост, который описывает, как округлить DateTime (этот пример округляется до минут, но основной принцип тот же).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...