tags:

views:

319

answers:

10

So, here's the scenario. I have a file with a created time, and I want to choose a time from a list of times that that file's created time is closest or equal too...what would be the best way to accomplish this?

+2  A: 

get the difference of your file creatime and every time in your list and sort the absolute value of each time difference. the first one should be the answer you are looking for.

ps
+1  A: 

Use the minimum absolute time difference between the file time and the time in the list. You might get 2 entries being the same, and then you would need a different method to differ between these.

astander
A: 
var creationTimes = new [] {DateTime.Now.AddDays(-1), DateTime.Now.AddDays(-2)};
FileInfo fi = new FileInfo("C:/test.xml");
var closestTime = creationTimes
    .OrderBy(c => Math.Abs(c.Subtract(fi.CreationTime).Days))
    .First();
Yuriy Faktorovich
Is there a reason for the downvote?
Yuriy Faktorovich
+1  A: 

Something like this:

DateTime fileDate, closestDate;
ArrayList theDates;
int min = int.MaxValue;

foreach (DateTime date in theDates)
 if (Math.Abs(date.Ticks- fileDate.Ticks) < min)
 {
   min = date.Ticks- fileDate.Ticks;
   closestDate = date;
 }
luvieere
The other answers that sort the list of time diffs just to find the smallest one are engaging in overkill. This approach is the most straightforward, although it's missing a Math.Abs() call.
JeffK
@JeffK it depends on what you find more readable. Although this approach is likely to be faster, but needs both a Math.Abs and .Days or something for the TimeSpan.
Yuriy Faktorovich
Math.Abs() added, .Days unnecessary, it works well as it is.
luvieere
@luvieere: It doesn't work well as it is. `Math.Abs` won't accept `TimeSpan` arguments.
LukeH
Sorry about the omission, I remembered it wrong and I'm not at my development station right now so I could test.
luvieere
@Yuriy - Readability first, I would agree. In that vein, I actually find this solution **more** readable than the various "sort, then select the first one" versions. For me, the sort makes big yellow question marks start bouncing over my head, as I wonder, "Why is this being sorted? What am I missing" And then there's the efficiency argument. Sorting is O(N log N) at best, while a simple scan is O(N).
JeffK
I'm liking this so far, but I have a few issues...first, I get a "Use of unassigned local variable on the int min;...then, date.Milliseconds doesn't work, but date.Millisecond does; however, date.Millisecond is always zero...
sunmorgus
This answer is completely wrong. The `millisecond` property goes from 0 to 999 every second. If the file was created at 12:00:00.001, this method will show 6:55:32.005 as being closer than 12:01:01.500.
Iceman
Furthermore, if you initialize `min = 0`, no absolute value will ever be less than that.
Iceman
I edited the answer for correctness, thank you for the helpful suggestions. I came up with the basic idea, @Kevin came up with corrective input, I leave it to the OP to select the accepted answer. Anyway, Kev, +1.
luvieere
You could also consider breaking the loop if they are exactly the same. You will not find anything closer.
Jonas Elfström
A: 
var min = listoftimes.Select(
    x => new { diff = Math.Abs((x - timeoffile).Ticks), time = x}).
    OrderBy(x => x.diff).
    First().time;

Note: Assumes at least 1 entry in listoftimes.

leppie
`Math.Abs` won't accept `TimeSpan` arguments.
LukeH
Of course :o) Fixed now.
leppie
+5  A: 

How often will you be doing this with the same list of times? If you're only doing it once, the fastest way is probably to just scan through the list and keep track of the closest time you've seen yet. When/if you encounter a time that's closer, replace the "closest" with that closer time.

If you're doing it very often, you'd probably want to sort the list, then use a binary search.

Jerry Coffin
+1  A: 
var closestTime = listOfTimes
                  .OrderBy(t => Math.Abs((t - fileCreateTime).Ticks))
                  .First();

If you don't want the performance overhead of the OrderBy call then you could use something like the MinBy extension method from MoreLINQ instead:

var closestTime = listOfTimes
                  .MinBy(t => Math.Abs((t - fileCreateTime).Ticks));
LukeH
+1  A: 
var closestTime = (from t in listOfTimes
                   orderby (t - fileInfo.CreationTime).Duration()
                   select t).First();
Thomas Levesque
+1  A: 

Not an answer, but a question regarding the various LINQ solutions proposed above. How efficient is LINQ? I have not written any "real" programs with LINQ yet, so I'm not sure on the performance.

In this example, the "listOfTimes" collection implies that we have already iterated over some file system based objects to gather the times. Would it have been more efficient to do the analysis during the iteration instead of later in LINQ? I recognize that these solutions may be more "elegant" or nicely abstract the "collection as database" idea, but I tend to choose efficiency (must be readable though) over elagance in my programming. Just wondering if the cost of LINQ might outweigh the elegance here?

cdkMoose
In general, LINQ's performance is pretty good. Usually not as fast as the equivalent plain loop, but often close enough that you don't really need to worry about it.
LukeH
The problem with all the LINQy answers here (including my own) is that they need to perform an `OrderBy` on the collection which will be much slower than a single O(N) dash using a loop.
LukeH
It's up to the OP to decide whether that performance penalty is an acceptable trade-off, *in their particular situation*, for LINQ's improved readability etc.
LukeH
Thanks for the feedback. There are certainly cases where I can see LINQ really paying off, but I'm always leary of the "wow" factor of new technologies vs good fundamental design decisions.
cdkMoose
+2  A: 

The accepted answer is completely wrong. What you want is something like this:

  DateTime fileDate, closestDate;
  List<DateTime> theDates;

  fileDate = DateTime.Today;       //set to the file date
  theDates = new List<DateTime>(); //load the date list, obviously

  long min = Math.Abs(fileDate.Ticks - theDates[0].Ticks);
  long diff;
  foreach (DateTime date in theDates)
  {
    diff = Math.Abs(fileDate.Ticks - date.Ticks);
    if (diff < min)
    {
      min = diff;
      closestDate = date;
    }
  }
Iceman