Ответ 1
Я использую методы TimeSpan.FromXXX, чтобы сделать что-то вроде этого:
if((myDate - myOtherDate) > TimeSpan.FromSeconds(10))
{
//Do something here
}
По умолчанию С# сравнивает объекты DateTime с типом 100ns. Однако моя база данных возвращает значения DateTime до ближайшей миллисекунды. Какой лучший способ сравнить два объекта DateTime в С# с использованием указанного допуска?
Изменить: я имею дело с проблемой усечения, а не с округлением. Как указывает Джо ниже, проблема округления приведет к появлению новых вопросов.
Решение, которое работает для меня, представляет собой комбинацию ниже.
(dateTime1 - dateTime2).Duration() < TimeSpan.FromMilliseconds(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
}
Класс расширения также содержит статический допуск по умолчанию, чтобы вы могли установить допуск для всего проекта и использовать метод "Внутри" без параметров:
DateTimeTolerance.SetDefault(TimeSpan.FromMinutes(1));
if(d1 == d2.Within()) { // Uses default tolerance
// TRUE! Do whatever
}
У меня есть несколько модульных тестов, но это будет слишком много кода для вставки здесь.
Вам нужно удалить компонент миллисекунд из объекта даты. Один из способов:
DateTime d = DateTime.Now;
d.Subtract(new TimeSpan(0, 0, 0, 0, d.Millisecond));
Вы также можете вычесть два дня
d.Subtract(DateTime.Now);
Это вернет объект времени, который вы можете использовать для сравнения дней, часов, минут и секунд, чтобы увидеть разницу.
if (Math.Abs(dt1.Subtract(dt2).TotalSeconds) < 1.0)
По умолчанию С# сравнивает объекты DateTime с миллисекундой.
На самом деле разрешение составляет отметку 100 нс.
Если вы сравниваете два значения DateTime из базы данных с разрешением 1 с, никаких проблем.
Если вы сравниваете DateTime с другим источником (например, текущим DateTime с использованием DateTime.Now), то вам нужно решить, как вам нужно обрабатывать доли секунды. Например. округлены до ближайшего или усечены? Как округлить, если это ровно полсекунды.
Я предлагаю вам округлить или усечь до целого числа секунд, а затем сравнить со значением из базы данных. Вот сообщение, в котором описывается, как округлить DateTime (этот пример округляется до минут, но главный - тот же).
У меня была аналогичная проблема с исходным вопросом, но чтобы сделать вещи более интересными, я сохранял и извлекал 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
}