开发者

Ignore milliseconds when comparing two datetimes

开发者 https://www.devze.com 2023-03-27 16:58 出处:网络
I am comparing the LastWriteTim开发者_JAVA技巧e 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 actua

I am comparing the LastWriteTim开发者_JAVA技巧e 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;
}


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())


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


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

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


TimeSpan difference = dtNew - dtOrig;
if (difference >= TimeSpan.FromSeconds(1))
{
    ...
}


You can subtract them, to get a TimeSpan.

Then use TimeSpan.totalSeconds()


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
    }


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


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.


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.


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


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;


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


cast sortable strings and compare. simple and run well.

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


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


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消