97

I am comparing the LastWriteTime of two files, however it is always failing because the file I downloaded off the net always has milliseconds set at 0, and my original file has an actual value. Is there a simple way to ignore the milliseconds when comparing?

Here's my function:

//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;
}
starball
  • 20,030
  • 7
  • 43
  • 238
Eros Nikolli
  • 1,011
  • 1
  • 7
  • 8

15 Answers15

124

I recommend you use an extension method:

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

then its just:

if (dtOrig.TrimMilliseconds() == dtNew.TrimMilliseconds())
Eternal21
  • 4,190
  • 2
  • 48
  • 63
Dean Chalk
  • 20,076
  • 6
  • 59
  • 90
56

Care should be taken, if dt has non-zero microseconds (fractions of millis). Setting only milliseconds to zero is not enough.
To set millis and below to zero (and get a succesfull comparison), the code would be:

dt = dt.AddTicks(-dt.Ticks % TimeSpan.TicksPerSecond); // TimeSpan.TicksPerSecond=10000000
Peter Ivan
  • 1,467
  • 2
  • 14
  • 27
  • 2
    This is the best solution. Fast, and preserves `dt.Kind`. I know it gives the same in all cases, but to me it is more natural to use `dt = dt.AddTicks(-(dt.Ticks % TimeSpan.TicksPerSecond));`, i.e. use `%` operator first, and then negate. That is because the behavior of `%` with a negative first operand seems a bit confusing to me, so I prefer my version where `%` operates on two positive operands. – Jeppe Stig Nielsen Jul 28 '16 at 11:23
  • @JeppeStigNielsen: Your code is exactly the same as mine. According to operator precedence, you multiply and/or divide and just then add/negate. So the parentheses change nothing, just clarify the well-established logic. – Peter Ivan Jul 29 '16 at 11:22
  • No that is incorrect. In rare cases (that cannot occur in your use above, so your use is fine), there is a difference. Consider this code (where `a` and `b` must not be declared `const`): `long a = long.MinValue; long b = 10L; long x = (-a) % b; long y = -(a % b); long z = -a % b;` Here the value of `x` will be negative, because negation of `a` gives `a` again, and a negative value `%`-ed on a positive value gives a negative value. But the value of `y` is positive, because this time we negate a negative result between `-10` and `0`, and that gives a positive number. Now check `z` to see it! – Jeppe Stig Nielsen Jul 29 '16 at 12:40
47

Create a new DateTime value with the milliseconds component set to 0:

dt = dt.AddMilliseconds(-dt.Millisecond);
dtb
  • 213,145
  • 36
  • 401
  • 431
32
TimeSpan difference = dtNew - dtOrig;
if (difference >= TimeSpan.FromSeconds(1))
{
    ...
}
Paul Ruane
  • 37,459
  • 12
  • 63
  • 82
18

You can subtract them, to get a TimeSpan.

Then use TimeSpan.totalSeconds()

Sanjay Manohar
  • 6,920
  • 3
  • 35
  • 58
8

This is overkill for a single Truncate, but if you have several and of various types you could do this using the generalized Extension Method below:

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

More general Use Extension method:

    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
  • 12,878
  • 4
  • 30
  • 43
  • 4
    With Peter Ivan's solution, it is natural to just do `public static DateTime TruncateTo(this DateTime dt, long truncateResolution) { return dt.AddTicks(-(dt.Ticks % truncateResolution)); }`. Much shorter and clearer. Then use like this: `DateTime dtSecs = DateTime.Now.TruncateTo(TimeSpan.TicksPerSecond); DateTime dtHrs = DateTime.Now.TruncateTo(TimeSpan.TicksPerHour);` and so on. You can even truncate to more strange resolutions, like multiples of 3 hours (`3 * TimeSpan.TicksPerHour`). – Jeppe Stig Nielsen Jul 28 '16 at 11:32
  • The dt.Kind should be set for each new DateTime – Doug Domeny Jun 09 '20 at 14:00
5

Here is the simplest way of doing this. You can control precision as you want.

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

and usage is pretty self-explanatory

var _ = AreEqual(a, b, precision: TimeSpan.FromSeconds(1));
Vladimir Gaevoy
  • 71
  • 2
  • 10
3

instead of trimming unrelevant DateTime parts via creating new DateTimes, compare only relevant parts:

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

I took answer by Dean Chalk as base for performance comparison, and results are:

  • CompareWith is a bit faster than TrimMilliseconds in case of equal dates

  • CompareWith is a faster than dates are not equal

my perf test (run in Console project)

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();
}
ASh
  • 34,632
  • 9
  • 60
  • 82
3

One way would be to create new dates, inputting the year, month, day, hour, minute, second into the constructor. Alternatively, you could simply compare each value separately.

drharris
  • 11,194
  • 5
  • 43
  • 56
2

Ether set the milliseconds in your other datetime to zero, or subtract one date from the other and just check the TotalMinutes property of the resulting time span.

Jonathan Wood
  • 65,341
  • 71
  • 269
  • 466
1

Simply you can use datetime format with the format you want, and convert it again to datetime as below,

//compare file's dates
        
        String format1 = @"yyyy-MM-dd HH:mm:ss"; // you also can avoid seconds if you want

        public bool CompareByModifiedDate(string strOrigFile, string strDownloadedFile)
        {
            
            //.here we will use the format

            DateTime dtOrig = Convert.ToDateTime(File.GetLastWriteTime(strOrigFile).ToString(format1));
            DateTime dtNew = Convert.ToDateTime(File.GetLastWriteTime(strDownloadedFile).ToString(format1));

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

You could create an extension method that would set the milliseconds to zero for a DateTime object

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

Then in your function

 if (dtOrig.ZeroMilliseconds() == dtNew.ZeroMilliseconds())
        return true;
     else
        return false;
Bobby Borszich
  • 11,639
  • 9
  • 37
  • 35
-1

cast sortable strings and compare. simple and run well.

    return string.Compare(dtOrig.ToString("s"), dtNew.ToString("s"), 
StringComparison.Ordinal) == 0;
Nuri YILMAZ
  • 4,291
  • 5
  • 37
  • 43
-3

The most straightforward way to truncate time is to format it and parse on the units that you want:

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

DOK's method re-written

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
  • 1
  • 1
  • 2
    Run your code in the UK, Europe or Australia and see what happens. I'm not a fan of this approact anyway, but you need to be using an invariant culture or `DateTime.ParseExact` for this to be even remotely reliable. – Simon MᶜKenzie Jan 14 '16 at 00:02
-6

Don't know why almost all programmers needs extra lines to return a bool value from a function with a bool expression.

instead

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

you can always just use

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

if the expression is true it returns true else false.

Mickey Mouse
  • 723
  • 6
  • 4