60

I have collection of TimeSpans, they represent time spent doing a task. Now I would like to find the average time spent on that task. It should be easy but for some reason I'm not getting the correct average.

Here's my code:

private TimeSpan? GetTimeSpanAverage(List<TimeSpan> sourceList)
{
    TimeSpan total = default(TimeSpan);

    var sortedDates = sourceList.OrderBy(x => x);

    foreach (var dateTime in sortedDates)
    {
        total += dateTime;
    }
    return TimeSpan.FromMilliseconds(total.TotalMilliseconds/sortedDates.Count());
}
wonea
  • 4,783
  • 17
  • 86
  • 139
hs2d
  • 6,027
  • 24
  • 64
  • 103
  • Please provide sample data and what result you are getting and what result you are expecting. BTW: The ordering is not necessary. – Daniel Hilgarth Jan 13 '12 at 08:22

3 Answers3

109

You can use the Average overload that takes a collection of long in parameter:

double doubleAverageTicks = sourceList.Average(timeSpan => timeSpan.Ticks);
long longAverageTicks = Convert.ToInt64(doubleAverageTicks);

return new TimeSpan(longAverageTicks);
vc 74
  • 37,131
  • 7
  • 73
  • 89
  • This gives me a error: cannot convert from 'double' to 'long' – hs2d Jan 13 '12 at 08:43
  • 1
    Instead of Ticks you can also use `TotalSeconds` and then use `TimeSpan.FromSeconds` – V4Vendetta Jan 13 '12 at 08:50
  • @V4Vendetta Yes you can but you'll loose the sub second components which can be useful if your are timing short actions – vc 74 Jan 13 '12 at 08:51
  • @vc74 Well in that case you can have TotalMilliseconds, also in your case you are casting double to long so the loss is happening in this case too :) – V4Vendetta Jan 13 '12 at 08:54
  • @V4Vendetta Yes but only once the average has been calculated which is better than for every timespan. – vc 74 Jan 13 '12 at 08:55
12
var average = new TimeSpan(sourceList.Select(ts => ts.Ticks).Average());

Note, your method returns a Nullable, but doesn't need to, unless you want to return null if the source list is empty, in which case just do a separate check first.

George Duckett
  • 31,770
  • 9
  • 95
  • 162
  • 5
    There is no `Enumerable.Average()` overload that returns `long` value, as well as there is no TimeSpan constructor that accept double as the argument, so the example is not building for me. To make it compile I was forced to add an explicit cast `(long)` of the argument: `var average = new TimeSpan((long)sourceList.Select(ts => ts.Ticks).Average());` – Konard Aug 20 '19 at 15:51
4

In Addition to the above answer, I would suggest you take an average on the Seconds or MilliSeconds level (depending on what you require)

sourceList.Average(timeSpan => timeSpan.ToTalMilliseconds)

Now using this value you could arrive at the new TimeSpan using

TimeSpan avg = TimeSpan.FromMilliseconds(double value here)
wonea
  • 4,783
  • 17
  • 86
  • 139
V4Vendetta
  • 37,194
  • 9
  • 78
  • 82
  • 2
    I just want to warn everyone that if your average is not even one millisecond, than because of [some precision problems](http://stackoverflow.com/a/5450832/1108200) your new average `TimeSpan` will be always 0. – Artholl Oct 29 '15 at 12:44
  • @Artholl That was my exact problem – Paul C Nov 17 '16 at 21:35