Подтвердить что ты не робот

Как вы сравниваете объекты DateTime с использованием заданного допуска в С#?

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

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

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

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

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

4b9b3361

Ответ 1

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

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

Ответ 2

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

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

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

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

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

Ответ 3

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

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

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

d.Subtract(DateTime.Now);

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

Ответ 4

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

Ответ 5

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

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

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

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

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

Ответ 6

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

Мне понравился ответ 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 test, чтобы убедиться, что все работает правильно:

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