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

4

6

I have items with the properties :

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

And I want to calculate the total of the difference between all the datetimes of each client with group by , but this :

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

Doesn't work since Sum doesn't work well for TimeSpan , which is the return value of the difference between two DateTimes object .

Any suggestion how can I get the total TimeSpan of each client ?

Thanks

Booklet answered 28/5, 2015 at 6:42 Comment(3)
What do you mean "doesn't work well"?Rodomontade
@PanagiotisKanavos Enumerable.Sum doesn't have a Timespan overloadMcnary
@PanagiotisKanavos: It means that you cannot convert TimeSpan to Decimal , when you use Sum .Booklet
R
4

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.

Rodomontade answered 28/5, 2015 at 7:3 Comment(0)
D
1

In my case, all proposed solutions didn't work. They caused error related with LINQ restrictions. So, my workaround looks like this:

var appointments = (from apps in DbContext.ClientAppointments
                           where apps.StartDate.Value.Date == date.Date
                           select new
                           {
                               SpecialistId = apps.SpecialistId,
                               Duration = (apps.EndDate.Value - apps.StartDate.Value).TotalSeconds
                           }).ToList();

        var result = (from apps in appointments
                     group apps by apps.SpecialistId into g
                     select new AppointmentsDurationDailyDto
                      {
                          SpecialistId = g.Key ?? 0,
                          Date = date.Date,
                          Duration = g.Sum(apps => apps.Duration)
                      }).ToList();

In this solution first .ToList(); is important to make next grouping statement on client. In case if you need to get TimeSpan Duration you can easily convert it back by using TimeSpan.FromSeconds(Duration)

Delftware answered 16/12, 2020 at 9:42 Comment(0)
T
0

You can use the .TotalMilliseconds Property

var retVal  = (from t items group t by ClientId  into z     
                              select new
                              {
                                  ClientId = z.Key,
                                  TimeSpanClientTotal = z.Sum(h => (h.EndDateTime - h.StartDateTime).TotalMilliseconds)
                                  }).ToList();
Tiaratibbetts answered 28/5, 2015 at 6:44 Comment(0)
E
0

You can write it like that:

 h.EndDateTime.Subtract(h.StartDateTime).TotalDays();
Entelechy answered 28/5, 2015 at 6:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.