Sum of Timespans in C#

Sum of TimeSpans in C#

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

Sum of multiple TimeSpan

30.97 is the correct number of hours. It does not mean "30 hours and 97 minutes".

30.97 hours is 30 hours and 58 minutes. 58 / 60 is roughly 0.97.

I think you just need to format your string properly. One way to format it is:

@"{(int)yourTimeSpan.TotalHours}:{yourTimeSpan.Minutes}"

Sum of Timespans using LINQ to Entities

I don't think you'll be able to do this very easily with Linq-to-SQL/EF using straight linq becuase of PossionTime.Ticks. C# DateTime operations are very limited when translating into sql operations.

If your only requirement is to support pagination, you could make it easy on yourself by doing your WeaponItem projection after your pagination operation and have pulled you the page into memory:

return stats
.GroupBy(x => x.Id)
.OrderByDescending(x => x.Sum(a => a.Kills))
// paginatation
.Skip(pageNumber * pageSize)
.Take(pageSize)
// pull paged result set into memory to make life easy
.ToList()
// transform into WeaponItem
.Select(x => new WeaponItem
{
PossessionTime = new TimeSpan(x.Sum(p => p.PossessionTime.Ticks))
});

Alternatively, you could change your WeaponItem class to store PossesionTimeTicks and then offer PossesionTime as a computed property. That might get around the restricton:

public class WeaponItem
{
public long PosseionTimeTicks {get;set;}
public TimeSpan PossesionTime {get{ return new TimeSpan(PosseionTimeticks); }
}

Then I think you should be able to keep your query as an IQueryable:

return stats
.GroupBy(x => x.Id)
.OrderByDescending(x => x.Sum(a => a.Kills))
.Select(x => new WeaponItem
{
PossessionTimeTicks = x.Sum(p => p.PossessionTime.Ticks)
});

LINQ: add/sum all timespans in a collection together

The problem is you are trying to call TimeSpan.FromSeconds in the database. This is a .NET method and can't be translated. This is untested, but to fix try this:

        var model = _db.Tracks
.Where(r => r.UserID == WebSecurity.CurrentUserId)
.Select(x => new
{
averagePace = Math.Round(26.8224 / x.locations.Average(y => y.speed), 2),
createdDate = x.createdDate,
FirstName = x.UserProfile.FirstName,
totalDistance = x.totalDistance,
totalDuration = x.totalDuration,
trackId = x.TrackID
}).ToList()
.Select(x => new iOSHistoryListViewModel{
averagePace = x.averagePace,
createdDate = x.createdDate,
FirstName = x.FirstName,
totalDistance = Math.Round(x.totalDistance / 1609.344, 2),
totalDuration = TimeSpan.FromSeconds(x.totalDuration),
trackId = x.trackId
})
.OrderByDescending(x => x.createdDate)
.ToList();

The key here is that the first Select throws in the data returned from the data source into a list. The list is then in .NET memory where you can do any .NET operations. Then send that result set to a new list as well. That should get rid of your exception about .FromSeconds

How to sum Timespan of subtraction between two datetimes in Linq when using group by?

Enumerable.Sum is just an extension method you call on an IEnumerable. There is nothing special to it so you can easily create another extension method that sums timespans:

static class TimeSpanExtensions
{
public static TimeSpan Sum<TSource>(this IEnumerable<TSource> enumerable,
Func<TSource,TimeSpan?> func )
{
return enumerable.Aggregate(TimeSpan.Zero, (total, it) =>
total+=(func(it)??TimeSpan.Zero);
}
}

Assuming your class definition is

class Record
{
public int ClientId { get; set; }
public DateTime StartDateTime { get; set; }
public DateTime EndDateTime { get; set; }

public Record(int clientId, DateTime startDateTime, DateTime endDateTime)
{
ClientId = clientId;
StartDateTime = startDateTime;
EndDateTime = endDateTime;
}
}

You can write the same code you would for the numeric types:

var items = new[] {
new Record(1, DateTime.Now, DateTime.Now.AddHours(1)),
new Record(1, DateTime.Now, DateTime.Now.AddHours(1)),
new Record(1, DateTime.Now, DateTime.Now.AddHours(1))};
var total=items.Sum(h=>(h.EndDateTime-h.StartDateTime));

var grouped= (from t in items
group t by t.ClientId into z
select new
{
ClientId = z.Key,
TimeSpanClientTotal = z.Sum(h => (h.EndDateTime - h.StartDateTime))
}).ToList();

You can also use Enumerable.Aggregate directly:

var total= items.Aggregate(TimeSpan.Zero, (current, it) => 
current += (it.EndDateTime-it.StartDateTime));

The code can be uglier but you can do a lot more than simple addition.

Sum method extension for IEnumerableTimeSpan

EDIT Thanks for clarifying what you're trying to achieve. I'm still not sure I really got it, so this will be my last attempt at it :-)

    public static TimeSpan Sum<TSource>(this IEnumerable<TSource> source, 
Func<TSource, TimeSpan> selector)
{
var ts = new TimeSpan();
return source.Aggregate(ts, (current, entry) => current + selector(entry));
}

I'm not sure what you try to indicate by TotalHours, because obviously, the sum of multiple time spans (that could include hours, minutes, days, seconds, etc. each) expressed as total hours is something else, as just summing up the Hours property of some timespans. I decided, that you meant summing up the full instances - you can access the required TotalHours, etc. information from the resulting TimeSpan.

TimeSpan is immutable, just like with DateTime, a common gotcha is, that you need to use the result of the Add method.

public static TimeSpan Sum(this IEnumerable<TimeSpan> source) {
TimeSpan sum = new TimeSpan(0, 0, 0);
foreach (TimeSpan v in source) {
sum = sum.Add(v);
}
return sum;
}

Also note, that TimeSpan is a value type (struct), thus can never by null. So that check against null in your code is superflous.

The LINQ version would look something like this:

    public static TimeSpan Sum(this IEnumerable<TimeSpan> source)
{
TimeSpan sum = new TimeSpan(0, 0, 0);
return source.Aggregate(sum, (current, v) => current.Add(v));
}

Again, you need to use the result of Add method.

Entiity Framework 7 / LINQ - Get sum of timespan objects

So, I ended up taking the approach of editing my Video model with two new properties

public int VideoMinutes { get; set; }
public int VideoSeconds { get; set; }

then from NReco.GetMediaInfo I get my TimeSpan object, from that I populate my two new fields as properties from the constructed TimeStamp object.

That then allows me to do the following in my HomeController

        var totalMinsOfVideo = _context.Videos.Select(v => v.VideoMinutes).Sum();
var totalSecsOfVideo = _context.Videos.Select(v => v.VideoSeconds).Sum();
var total = new TimeSpan(0, totalMinsOfVideo, totalSecsOfVideo);

It may not be the cleanest solution but this is providing me what I need currently.

LINQ (C#) Sum a String in a HH:mm format

Here you have to use the combinaison of 2 things:

  1. Convert the string into an usable format, that's TimeSpan

    using How to Convert string “07:35” (HH:MM) to TimeSpan

  2. The Sum the TimeSpan using Sum of TimeSpans in C#

public class Toto
{
public int ProjectID { get; set; }
public string ProjectTime { get; set; }
}
static void Main(string[] args)
{
var tmpList = new[]{
new Toto{ProjectID=1, ProjectTime="00:10" },
new Toto{ProjectID=2, ProjectTime="23:20" },
new Toto{ProjectID=2, ProjectTime="23:30" },
new Toto{ProjectID=1, ProjectTime="00:40" },
};

var overviewList = tmpList.GroupBy(x => x.ProjectID)
.Select(x => new
{
ProjectID = x.Key,
ProjectTime = new TimeSpan(x.Sum(o => TimeSpan.Parse(o.ProjectTime).Ticks))
});

Result:

ProjectID =1, ProjectTime=00:50:00

ProjectID =2, ProjectTime=1.22:50:00

That 1.22:50:00 May not be the format you expected. But Timespan Custom format won't be usefull here.

If you need to get the same string format again. You can use, but keep the format TimeSpan as long as you use it use that only for display.

 (int)X.ProjectTime.TotalHours +":" +X.ProjectTime.Minutes

ProjectID =1, ProjectTime=0:50

ProjectID =2, ProjectTime=46:50

Calculation of sum of hours in 24h format with incorrect C#

In your function public static String ConversioneTimeSpantoString(TimeSpan tm)

Change

tm.Hours //-> the remining hours in this timespan (minus days)

To

tm.TotalHours //-> the sum of all hours in this timespan (included days, months, years)


Related Topics



Leave a reply



Submit