Sum of TimeSpans in C#
Solution 1:
Unfortunately, there isn't a an overload of Sum
that accepts an IEnumerable<TimeSpan>
. Additionally, there's no current way of specifying operator-based generic constraints for type-parameters, so even though TimeSpan
is "natively" summable, that fact can't be picked up easily by generic code.
One option would be to, as you say, sum up an integral-type equivalent to the timespan instead, and then turn that sum into a TimeSpan
again. The ideal property for this is TimeSpan.Ticks
, which round-trips accurately. But it's not necessary to change the property-type on your class at all; you can just project:
var totalSpan = new TimeSpan(myCollection.Sum(r => r.TheDuration.Ticks));
Alternatively, if you want to stick to the TimeSpan's +
operator to do the summing, you can use the Aggregate
operator:
var totalSpan = myCollection.Aggregate
(TimeSpan.Zero,
(sumSoFar, nextMyObject) => sumSoFar + nextMyObject.TheDuration);
Solution 2:
This works well (code based on Ani's answer)
public static class StatisticExtensions
{
public static TimeSpan Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, TimeSpan> selector)
{
return source.Select(selector).Aggregate(TimeSpan.Zero, (t1, t2) => t1 + t2);
}
}
Usage :
If Periods is a list of objects with a Duration property
TimeSpan total = Periods.Sum(s => s.Duration)
Solution 3:
I believe this is the cleanest LINQ extension:
public static class LinqExtensions
{
public static TimeSpan Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, TimeSpan> func)
{
return new TimeSpan(source.Sum(item => func(item).Ticks));
}
}
Usage is the same:
TimeSpan total = Periods.Sum(s => s.Duration)
Solution 4:
Here's what I tried and it worked:
System.Collections.Generic.List<MyObject> collection = new List<MyObject>();
MyObject mb = new MyObject();
mb.TheDuration = new TimeSpan(100000);
collection.Add(mb);
mb.TheDuration = new TimeSpan(100000);
collection.Add(mb);
mb.TheDuration = new TimeSpan(100000);
collection.Add(mb);
var sum = (from r in collection select r.TheDuration.Ticks).Sum();
Console.WriteLine( sum.ToString());
//here we have new timespan that is sum of all time spans
TimeSpan sumedup = new TimeSpan(sum);
public class MyObject
{
public TimeSpan TheDuration { get; set; }
}
Solution 5:
I put this in a class to add an extension method to a collection of timespans:
public static class Extensions:
{
public static TimeSpan TotalTime(this IEnumerable<TimeSpan> TheCollection)
{
int i = 0;
int TotalSeconds = 0;
var ArrayDuration = TheCollection.ToArray();
for (i = 0; i < ArrayDuration.Length; i++)
{
TotalSeconds = (int)(ArrayDuration[i].TotalSeconds) + TotalSeconds;
}
return TimeSpan.FromSeconds(TotalSeconds);
}
}
So now, I can write TotalDuration = (my LINQ query that returns a collection of timespan).TotalTime();
Voila!