Somatório de horas no Report Viewer - André Alves de Lima
Se você já precisou somar horas no Report Viewer, muito provavelmente você teve que fazer alguma gambiarra. Se tentarmos somar campos do tipo DateTime ou TimeStamp, o resultado não será o esperado. Dessa forma, temos que fazer esse tipo de somatório de horas no Report Viewer "na mão".
No vídeo de hoje eu mostro para você o jeito menos "gambiarrado" de se implementar esse tipo de somatório, onde convertemos toda a informação em segundos, somamos e depois formatamos o resultado adequadamente. Veja só o resultado no vídeo de hoje:
Como armazenamos informações de horas?
Quando temos que armazenar informações relacionadas a horas, eu consigo visualizar três alternativas:
– Campos DateTime: considerando somente a parte das horas, minutos e segundos, ignorando o conteúdo da data em si
– Campos TimeSpan: na minha opinião é a melhor estrutura para armazenar um intervalo de tempo, afinal, é justamente para isso que essa classe foi criada
– Campos decimais: o horário é convertido em horas decimais, onde, por exemplo, o valor 2.5 corresponderia a duas horas e meia
Tendo como base essa realidade, eu preparei um relatório extremamente simples com informações de horas nesses três formatos:
A estratégia de sumarização de horas
Para fazermos o somatório de horas no Report Viewer, nós seguiremos uma estratégia muito simples. Primeiro nós converteremos as informações de horas em segundos. Depois, nós adicionaremos esse total de segundos em um DateTime com base "zero horas". Com isso nós teremos a quantidade de horas, minutos e segundos, porém, nós perderemos a informação de dias, caso o total passe de 24 horas.
O cálculo da quantidade de dias pode ser feito dividindo a mesma somatória de segundos que utilizamos no passo anterior pela quantidade de segundos presentes em um dia (86400 segundos). Se pegarmos a parte inteira dessa divisão, teremos a quantidade de dias!
Somando horas decimais
Vamos começar com o somatório de horas decimais. Nesse caso, como temos que converter a informação para segundos, nós multiplicaremos tudo por 3600 (que é a quantidade de segundos presentes em uma hora). Em seguida, nós adicionamos esse total de segundos em um DateTime "zero hora" através da função "DateAdd". Por fim, nós calculamos a quantidade de dias dividindo a somatória de segundos por 86400 e considerando somente a parte inteira através da função "Floor".
Veja só como é que fica a expressão:
=Floor(Sum(Fields!HorasDecimal.Value) * 3600 / 86400) & ":" & Format(DateAdd("s", Sum(Fields!HorasDecimal.Value * 3600), "00:00:00"), "HH:mm:ss")
Somando TimeStamps
Para somarmos horas que estejam armazenadas em TimeStamps, o processo é um pouco mais simples, uma vez que nós temos a propriedade "TotalSeconds" que já retorna a quantidade total de segundos do TimeStamp. Apesar dessa propriedade não aparecer no IntelliSense, ela está disponível em tempo de execução e conseguimos utilizá-la sem problema algum. Nesse caso, a expressão fica assim:
=Floor(Sum(Fields!HorasTimeSpan.Value.TotalSeconds) / 86400) & ":" & Format(DateAdd("s", Sum(Fields!HorasTimeSpan.Value.TotalSeconds), "00:00:00"), "HH:mm:ss")
Somando DateTimes
O somatório de informações do tipo DateTime é muito parecido com o somatório de TimeStamps, uma vez que a classe DateTime possui a propriedade "TimeOfDay" que é justamente um TimeStamp representando a parte das horas, minutos e segundos do DateTime:
=Floor(Sum(Fields!HorasDateTime.Value.TimeOfDay.TotalSeconds) / 86400) & ":" & Format(DateAdd("s", Sum(Fields!HorasDateTime.Value.TimeOfDay.TotalSeconds), "00:00:00"), "HH:mm:ss")
E com isso temos o somatório de horas no Report Viewer tanto com DateTime quanto TimeStamp e valores decimais:
Apesar do Report Viewer não ter suporte nativo ao somatório de horas, você viu neste vídeo que esse tipo de sumarização é totalmente implementável utilizando algumas conversões e cálculos. Se convertermos todas as informações para segundos e depois adicionarmos o total em um DateTime "zero hora", nós teremos o resultado esperado, independentemente da estrutura de dados que você estiver utilizando para armazenar as horas, seja DateTime, TimeStamp ou valores decimais.
E você, já precisou somar horas no Report Viewer? Como é que você acabou saindo dessa situação? Você utilizou exatamente essas fórmulas ou acabou fazendo de alguma outra maneira diferente? Conte-nos mais detalhes na caixa de comentários logo abaixo!
Enviando SMS com C# e VB.NET Aplicações Android com Xamarin – Parte 6 de N – DatePicker e Spinner (entre outras pendências)
12 thoughts on "Somatório de horas no Report Viewer"
Parabéns pelo post. Já desenvolvi alguns projetos de cartão de ponto e estou, justamente, neste momento, desenvolvendo um outro, onde utilizarei dicas de um outro artigo seu (Tirando fotos com a WebCam em C#) e estava justamente pensando qual seria a melhor maneira de guardar os dados das horas.
Seu artigo, mais uma vez, veio bem a calhar! Acreito que utilizar o Timespan seja o melhor caminho.
Vou analisar esta situação e encaixar suas dicas.
Valeu! Abraços!!!
27 de outubro de 2017 às 5:37
Olá André! Mais uma vez, muito obrigado! :)
Eu também acho que o TimeSpan é a melhor estrutura para armazenar horas.. Se não der para armazenar em TimeSpan, eu armazenaria em decimal.. Só utilizaria DateTime em último caso..
Enfim, se ficar com alguma dúvida quando você for implementar essa funcionalidade, é só falar..
29 de outubro de 2017 às 14:51
Olá André. Parabéns pelo vídeo. Muito bem explicado e esclarecedor como sempre!
30 de outubro de 2017 às 8:32
Olá Carlos, muito obrigado! Que bom que você gostou.. :)
Qualquer dúvida, estamos aí..
Ola André parabens, seus artigos tem me ajudado muito, em especial nesse estou com um problema que não consigo resolver, ao somar horas cujo o valor da acima de 24 horas ele exibe com um numero de dias, gostaria que aparecesse somente o numero de horas por exemplo se deu 26 horas e 22 minutos aparecesse 26:22:00 ao inves de 1:2:22:00 é possível, obrigado
5 de janeiro de 2018 às 9:42
Olá Alan, muito obrigado pelo comentário!
Para conseguir o que você está querendo, você terá que fazer algum malabarismo com as expressões, uma vez que o próprio .NET não tem um formato de DateTime que exiba a hora dessa maneira (os formatos só aceitam horas até 24 horas)..
Enfim, eu fiz alguns ajustes no projeto de exemplo aqui e consegui chegar em um resultado.. Muito provavelmente as expressões podem ser simplificadas, mas dessa maneira eu consegui obter o resultado que você está querendo..
Se o campo estiver em horas decimais:
=((Floor(Sum(Fields!HorasDecimal.Value) * 3600 / 86400) * 24) + DateAdd("s", Sum(Fields!HorasDecimal.Value * 3600), "00:00:00").Hour) & ":" & Format(DateAdd("s", Sum(Fields!HorasDecimal.Value * 3600), "00:00:00"), "mm:ss")
Se o campo estiver em TimeStamp:
=((Floor(Sum(Fields!HorasTimeSpan.Value.TotalSeconds) / 86400) * 24) + DateAdd("s", Sum(Fields!HorasTimeSpan.Value.TotalSeconds), "00:00:00").Hour) & ":" & Format(DateAdd("s", Sum(Fields!HorasTimeSpan.Value.TotalSeconds), "00:00:00"), "mm:ss")
Se o campo estiver em DateTime:
=((Floor(Sum(Fields!HorasDateTime.Value.TimeOfDay.TotalSeconds) / 86400) * 24) + DateAdd("s", Sum(Fields!HorasDateTime.Value.TimeOfDay.TotalSeconds), "00:00:00").Hour) & ":" & Format(DateAdd("s", Sum(Fields!HorasDateTime.Value.TimeOfDay.TotalSeconds), "00:00:00"), "mm:ss")
Era exatamente isso que eu precisava deu certinho, muito obrigado!
8 de janeiro de 2018 às 8:38
Legal, Alan! Que bom que deu certo.. Qualquer coisa estamos aí..
12 de janeiro de 2018 às 0:49
Muito bom gostei estava realmente precisando obrigado
12 de janeiro de 2018 às 5:44
Que bom que você gostou do artigo.. Fico feliz por ter conseguido ajudar.. Qualquer dúvida, é só falar..
Não estou conseguindo Aplicar em meu crystal repoty. Teria como ajuda …
OBS.: Meu Campo que vem da tabela esta em string, e ele ja vem com o valor somado do dia agora tenho que somar Geral ou seja soma todo os dias do mes.