Ignorieren Sie Millisekunden, wenn Sie zwei Datumszeiten vergleichen

76

Dies ist wahrscheinlich eine dumme Frage, aber ich kann es nicht herausfinden. Ich vergleiche die LastWriteTime von zwei Dateien, dies schlägt jedoch immer fehl, da für die aus dem Netz heruntergeladene Datei immer Millisekunden auf 0 gesetzt sind und meine Originaldatei einen tatsächlichen Wert hat. Gibt es eine einfache Möglichkeit, die Millisekunden beim Vergleich zu ignorieren?

Hier ist meine Funktion:

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

Danke im Voraus

Eros Nikolli
quelle

Antworten:

41

Erstellen Sie einen neuen DateTime-Wert, wobei die Millisekunden-Komponente auf 0 gesetzt ist:

dt = dt.AddMilliseconds(-dt.Millisecond);
dtb
quelle
25
Warnung: Dies funktioniert nicht, wenn die DateTimeMikrosekunden ungleich Null sind. Siehe die Antworten von @ PeterIvan oder @ DeanChalk.
TheCloudlessSky
97

Ich empfehle Ihnen eine Erweiterungsmethode:

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

dann ist es nur:

if (dtOrig.TrimMilliseconds() == dtNew.TrimMilliseconds())
Dean Chalk
quelle
3
Vielleicht bewahren Sie das DateTimeKindauch, indem Sie dt.Kindam Ende von ctor hinzufügen
Luke Hutton
3
neue DateTime zurückgeben (dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, dt.Second, 0, dt.Kind);
Adamy
40

Vorsicht ist geboten, wenn dtdie Mikrosekunden ungleich Null (Millisekunden) sind. Es reicht nicht aus, nur Millisekunden auf Null zu setzen.
Um Millis und darunter auf Null zu setzen (und einen erfolgreichen Vergleich zu erhalten), wäre der Code:

dt = dt.AddTicks(-dt.Ticks % TimeSpan.TicksPerSecond); // TimeSpan.TicksPerSecond=10000000
Peter Ivan
quelle
1
Dies ist die beste Lösung. Schnell und konserviert dt.Kind. Ich weiß , dass es das gleiche in allen Fällen gibt, aber für mich ist es natürlich zu Verwendung dt = dt.AddTicks(-(dt.Ticks % TimeSpan.TicksPerSecond));, dh Nutzung %Operator zuerst, und dann negiert. Das liegt daran %, dass mir das Verhalten mit einem negativen ersten Operanden etwas verwirrend erscheint. Deshalb bevorzuge ich meine Version, in %der zwei positive Operanden bearbeitet werden.
Jeppe Stig Nielsen
@JeppeStigNielsen: Dein Code ist genau der gleiche wie meiner. Je nach Vorrang des Operators multiplizieren und / oder dividieren Sie und addieren / negieren dann. Die Klammern ändern also nichts, sondern verdeutlichen nur die etablierte Logik.
Peter Ivan
Nein, das ist falsch. In seltenen Fällen (die bei Ihrer Verwendung oben nicht auftreten können, sodass Ihre Verwendung in Ordnung ist) gibt es einen Unterschied. Betrachten Sie diesen Code (wo aund bdarf nicht deklariert werden const): long a = long.MinValue; long b = 10L; long x = (-a) % b; long y = -(a % b); long z = -a % b;Hier ist der Wert von xnegativ, weil die Negation von wieder aergibt aund ein negativer Wert %-ed auf einem positiven Wert einen negativen Wert ergibt. Aber der Wert von yist positiv, weil wir diesmal ein negatives Ergebnis zwischen -10und negieren 0, und das ergibt eine positive Zahl. Jetzt schau zes dir an!
Jeppe Stig Nielsen
27
TimeSpan difference = dtNew - dtOrig;
if (difference >= TimeSpan.FromSeconds(1))
{
    ...
}
Paul Ruane
quelle
Dies ist der Weg, IMHO zu gehen.
Larry
16

Sie können sie subtrahieren, um eine zu erhalten TimeSpan.

Dann benutze TimeSpan.totalSeconds()

Sanjay Manohar
quelle
7

Dies ist ein Overkill für ein einzelnes Abschneiden. Wenn Sie jedoch mehrere und verschiedene Typen haben, können Sie dies mithilfe der folgenden allgemeinen Erweiterungsmethode tun:

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

Allgemeiner Verwenden Sie die Erweiterungsmethode:

    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
    }
deepee1
quelle
4
Mit der Lösung von Peter Ivan ist es selbstverständlich, dies einfach zu tun public static DateTime TruncateTo(this DateTime dt, long truncateResolution) { return dt.AddTicks(-(dt.Ticks % truncateResolution)); }. Viel kürzer und klarer. Dann verwenden Sie wie folgt: DateTime dtSecs = DateTime.Now.TruncateTo(TimeSpan.TicksPerSecond); DateTime dtHrs = DateTime.Now.TruncateTo(TimeSpan.TicksPerHour);und so weiter. Sie können sogar auf seltsamere Auflösungen kürzen, z. B. ein Vielfaches von 3 Stunden ( 3 * TimeSpan.TicksPerHour).
Jeppe Stig Nielsen
Die dt.Kind sollte für jede neue DateTime
Doug Domeny
3

Eine Möglichkeit wäre, neue Daten zu erstellen und das Jahr, den Monat, den Tag, die Stunde, die Minute und die Sekunde in den Konstruktor einzugeben. Alternativ können Sie einfach jeden Wert einzeln vergleichen.

Drharris
quelle
3

Hier ist der einfachste Weg, dies zu tun. Sie können steuern, precisionwie Sie möchten.

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

und die Verwendung ist ziemlich selbsterklärend

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

Ether setzt die Millisekunden in Ihrer anderen Datumszeit auf Null oder subtrahiert ein Datum vom anderen und überprüft einfach die TotalMinutesEigenschaft der resultierenden Zeitspanne.

Jonathan Wood
quelle
1

Sie können eine Erweiterungsmethode erstellen, mit der die Millisekunden für ein DateTime-Objekt auf Null gesetzt werden

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

Dann in Ihrer Funktion

 if (dtOrig.ZeroMilliseconds() == dtNew.ZeroMilliseconds())
        return true;
     else
        return false;
Bobby Borszich
quelle
1
Ihre Methode gibt also eine DateTime zurück, ist jedoch als Rückgabe einer Zeichenfolge definiert.
Ash Burlaczenko
3
Ich weiß nicht, warum alle Programmierer zu viele Zeilen benötigen, wenn sie eine Bool-Funktion mit
Mickey Mouse
Der Wert.Kind sollte in der neuen DateTime
Doug Domeny
1

DateTimeVergleichen Sie nur relevante Teile, anstatt nicht relevante DateTime-Teile durch Erstellen neuer s zu kürzen :

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

Ich habe die Antwort von Dean Chalk als Grundlage für den Leistungsvergleich genommen. Die Ergebnisse sind:

  • CompareWithist etwas schneller als TrimMillisecondsbei gleichen Daten

  • CompareWith ist ein schneller als Daten sind nicht gleich

Mein Perf-Test (im Konsolenprojekt ausführen)

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();
}
Asche
quelle
-1

Wirf sortierbare Strings und vergleiche. einfach und gut laufen.

    return string.Compare(dtOrig.ToString("s"), dtNew.ToString("s"), 
StringComparison.Ordinal) == 0;
Nuri YILMAZ
quelle
-3

Der einfachste Weg, die Zeit zu verkürzen, besteht darin, sie zu formatieren und die gewünschten Einheiten zu analysieren:

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

DOKs Methode neu geschrieben

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;
    }
M. Smith
quelle
2
Führen Sie Ihren Code in Großbritannien, Europa oder Australien aus und sehen Sie, was passiert. Ich bin sowieso kein Fan dieses Ansatzes, aber Sie müssen eine unveränderliche Kultur verwenden oder DateTime.ParseExactdamit dies auch nur aus der Ferne zuverlässig ist.
Simon MᶜKenzie
-6

Ich weiß nicht, warum fast alle Programmierer zusätzliche Zeilen benötigen, um einen Bool-Wert von einer Funktion mit einem Bool-Ausdruck zurückzugeben.

stattdessen

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

Sie können immer nur verwenden

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

Wenn der Ausdruck wahr ist, wird true zurückgegeben, andernfalls false.

Mickey Maus
quelle
Woher hast du ZeroMilliseconds ()?
Purusartha