Ігноруйте мілісекунди при порівнянні двох дат


76

Це, мабуть, глупе запитання, але я, здається, не можу зрозуміти. Я порівнюю LastWriteTime двох файлів, однак це завжди не вдається, оскільки файл, який я завантажив з мережі, завжди має мілісекунди, встановлені на 0, і мій оригінальний файл має фактичне значення. Чи є простий спосіб ігнорувати мілісекунди при порівнянні?

Ось моя функція:

//compare file's dates
public bool CompareByModifiedDate(string strOrigFile, string strDownloadedFile)
{
     DateTime dtOrig = File.GetLastWriteTime(strOrigFile);
     DateTime dtNew = File.GetLastWriteTime(strDownloadedFile);

     if (dtOrig == dtNew)
        return true;
     else
        return false;
}

Спасибі заздалегідь


Відповіді:


42

Створіть нове значення DateTime з компонентом мілісекунд, встановленим на 0:

dt = dt.AddMilliseconds(-dt.Millisecond);

26
Попередження: це не спрацює, якщо DateTimeмікросекунда має ненульові мікросекунди. Дивіться відповіді @ PeterIvan або @ DeanChalk.
TheCloudlessSky

98

Я рекомендую використовувати метод розширення:

public static DateTime TrimMilliseconds(this DateTime dt)
{
    return new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, dt.Second, 0, dt.Kind);
}

тоді це просто:

if (dtOrig.TrimMilliseconds() == dtNew.TrimMilliseconds())

3
Можливо, збережіть DateTimeKindі це, додавши dt.Kindдо кінця ctor
Luke Hutton

3
повернути новий DateTime (dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, dt.Second, 0, dt.Kind);
Adamy

41

Слід бути обережним, якщо dtє ненульові мікросекунди (частки міліс). Встановлення нуля лише в мілісекундах недостатньо.
Щоб встановити нуль міліс і нижче (і отримати успішне порівняння), код буде таким:

dt = dt.AddTicks(-dt.Ticks % TimeSpan.TicksPerSecond); // TimeSpan.TicksPerSecond=10000000

1
Це найкраще рішення. Швидко і консервує dt.Kind. Я знаю, що це дає те саме у всіх випадках, але для мене це більш природно використовувати dt = dt.AddTicks(-(dt.Ticks % TimeSpan.TicksPerSecond));, тобто %спочатку використовувати оператор, а потім заперечувати. Це тому, що поведінка %з негативним першим операндом здається мені дещо заплутаною, тому я віддаю перевагу своїй версії, де %оперує двома позитивними операндами.
Jeppe Stig Nielsen

@JeppeStigNielsen: Ваш код точно такий же, як і мій. Відповідно до переваги оператора, ви множите та / або ділите, а потім додаєте / заперечуєте. Отже, дужки нічого не змінюють, просто з’ясуйте усталену логіку.
Петро Іван

Ні, це неправильно. У рідкісних випадках (що не може трапитись під час використання вище, тож використання є нормальним) є різниця. Розглянемо цей код (де aі bне повинен бути оголошений const): long a = long.MinValue; long b = 10L; long x = (-a) % b; long y = -(a % b); long z = -a % b;Тут значення xбуде негативним, оскільки заперечення aдає aзнову, а негативне значення %-ed на позитивному значенні дає негативне значення. Але значення yпозитивне, оскільки цього разу ми заперечуємо негативний результат між -10і 0, і це дає позитивне число. Тепер перевірте, zщоб побачити!
Jeppe Stig Nielsen



7

Це надмірно для одного Truncate, але якщо у вас декілька різних типів, ви можете зробити це, використовуючи узагальнений метод розширення нижче:

DateTime dtSecs = DateTime.Now.TruncateTo(Extensions.DateTruncate.Second);
DateTime dtHrs  = DateTime.Now.TruncateTo(Extensions.DateTruncate.Hour);

Більш загальний метод використання розширення:

    public static DateTime TruncateTo(this DateTime dt, DateTruncate TruncateTo)
    {
        if (TruncateTo == DateTruncate.Year)
            return new DateTime(dt.Year, 0, 0);
        else if (TruncateTo == DateTruncate.Month)
            return new DateTime(dt.Year, dt.Month, 0);
        else if (TruncateTo == DateTruncate.Day)
            return new DateTime(dt.Year, dt.Month, dt.Day);
        else if (TruncateTo == DateTruncate.Hour)
            return new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, 0, 0);
        else if (TruncateTo == DateTruncate.Minute)
            return new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, 0);
        else 
            return new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, dt.Second);

    }
    public enum DateTruncate
    {
        Year,
        Month,
        Day,
        Hour,
        Minute,
        Second
    }

4
З рішенням Петра Івана це природно просто робити public static DateTime TruncateTo(this DateTime dt, long truncateResolution) { return dt.AddTicks(-(dt.Ticks % truncateResolution)); }. Набагато коротше і чіткіше. Тоді використовуйте так: DateTime dtSecs = DateTime.Now.TruncateTo(TimeSpan.TicksPerSecond); DateTime dtHrs = DateTime.Now.TruncateTo(TimeSpan.TicksPerHour);і так далі. Ви навіть можете скоротити до більш дивних роздільних здатностей, таких як кратні 3 години ( 3 * TimeSpan.TicksPerHour).
Jeppe Stig Nielsen

Dt.Kind повинен бути встановлений для кожного нового часу часу
Дуг Домені

3

Одним із способів було б створити нові дати, ввівши рік, місяць, день, годину, хвилину, секунду в конструктор. Як варіант, ви можете просто порівняти кожне значення окремо.


3

Ось найпростіший спосіб зробити це. Ви можете керувати precisionяк завгодно.

bool AreEqual(DateTime a, DateTime b, TimeSpan precision)
{
    return Math.Abs((a - b).TotalMilliseconds) < precision.TotalMilliseconds;
}

і використання досить зрозуміло

var _ = AreEqual(a, b, precision: TimeSpan.FromSeconds(1));

2

Ефір встановіть мілісекунди в інший час часу на нуль або відніміть одну дату від іншої і просто перевірте TotalMinutesвластивість отриманого проміжку часу.


1

Ви можете створити метод розширення, який встановлюватиме нулі мілісекунд для об'єкта DateTime

public static DateTime ZeroMilliseconds(this DateTime value) {
  return new DateTime(value.Year, value.Month, value.Day, 
    value.Hours, value.Minutes, value.Seconds);
}

Тоді у вашій функції

 if (dtOrig.ZeroMilliseconds() == dtNew.ZeroMilliseconds())
        return true;
     else
        return false;

1
Отже, ваш метод повертає DateTime, але визначається як повернення рядка.
Ash Burlaczenko

3
Не знаю, чому всім програмістам потрібно занадто багато рядків, коли перевіряється функція bool за допомогою
Міккі Маус

Значення. Вид слід встановити в новому DateTime
Дуг Домені

1

замість того, щоб обрізати нерелевантні частини DateTime за допомогою створення нових DateTimes, порівняйте лише відповідні деталі:

public static class Extensions
{
    public static bool CompareWith(this DateTime dt1, DateTime dt2)
    {
        return
            dt1.Second == dt2.Second && // 1 of 60 match chance
            dt1.Minute == dt2.Minute && // 1 of 60 chance
            dt1.Day == dt2.Day &&       // 1 of 28-31 chance
            dt1.Hour == dt2.Hour &&     // 1 of 24 chance
            dt1.Month == dt2.Month &&   // 1 of 12 chance
            dt1.Year == dt2.Year;       // depends on dataset
    }
}

Я взяв відповідь Діна Крейда як основу для порівняння продуктивності, і результати такі:

  • CompareWith трохи швидше, ніж TrimMilliseconds у випадку рівних дат

  • CompareWith швидше, ніж дати не рівні

мій тест на виконання (запуск у проекті Console)

static void Main(string[] args)
{
    var dtOrig = new DateTime(2018, 03, 1, 10, 10, 10);
    var dtNew = dtOrig.AddMilliseconds(100);

    //// perf run for not-equal dates comparison
    //dtNew = dtNew.AddDays(1);
    //dtNew = dtNew.AddMinutes(1);

    int N = 1000000;

    bool isEqual = false;

    var sw = Stopwatch.StartNew();
    for (int i = 0; i < N; i++)
    {
        // TrimMilliseconds comes from 
        // https://stackoverflow.com/a/7029046/1506454 
        // answer by Dean Chalk
        isEqual = dtOrig.TrimMilliseconds() == dtNew.TrimMilliseconds();
    }
    var ms = sw.ElapsedMilliseconds;
    Console.WriteLine("DateTime trim: " + ms + " ms");

    sw = Stopwatch.StartNew();
    for (int i = 0; i < N; i++)
    {
        isEqual = dtOrig.CompareWith(dtNew);
    }
    ms = sw.ElapsedMilliseconds;
    Console.WriteLine("DateTime partial compare: " + ms + " ms");

    Console.ReadKey();
}

-1

киньте сортувальні струни та порівняйте. простий і добре працює.

    return string.Compare(dtOrig.ToString("s"), dtNew.ToString("s"), 
StringComparison.Ordinal) == 0;

-3

Найпростіший спосіб скоротити час - це відформатувати його та проаналізувати одиниці, які ви хочете:

var myDate = DateTime.Parse(DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss"));

Метод DOK переписаний

public bool CompareByModifiedDate(string strOrigFile, string strDownloadedFile)
    {
         DateTime dtOrig = DateTime.Parse(File.GetLastWriteTime(strOrigFile).ToString("MM/dd/yyyy hh:mm:ss"));
         DateTime dtNew = DateTime.Parse(File.GetLastWriteTime(strDownloadedFile).ToString("MM/dd/yyyy hh:mm:ss"));

         if (dtOrig == dtNew)
            return true;
         else
            return false;
    }

2
Запустіть свій код у Великобританії, Європі чи Австралії і подивіться, що станеться. Я все одно не прихильник цього підходу, але вам потрібно використовувати інваріантну культуру або DateTime.ParseExactщоб це було навіть віддалено надійно.
Simon MᶜKenzie

-6

Не знаю, чому майже всім програмістам потрібні додаткові рядки, щоб повернути значення bool із функції з виразом bool.

замість цього

if (dtOrig.ZeroMilliseconds() == dtNew.ZeroMilliseconds())
    return true;
 else
    return false;

Ви завжди можете просто використовувати

return dtOrig.ZeroMilliseconds() == dtNew.ZeroMilliseconds()

якщо вираз true, він повертає true ще false.


звідки ти взяв ZeroMilliseconds ()?
Пурусарта
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.