Dias e horas úteis
Mais um desafio que recentemente tive de superar, num projecto. Engraçado foi o quão simples é a solução e o quão rebuscada a mente consegue ser para a encontrar.
Neste caso uma simples classe em C# permite achar as horas uteis decorridas entre duas datas e horas, sendo estas passadas como objectos Datetime.
Outras particularidades da classe é asseitar como parametro os dias feriados (excludeDays), que com pouco código até podem vir de uma tabela de uma BD.
Achei engraçada a facilidade com que se consegue instanciar, fazer os calculos e obter o tempo decorrido com precisão ao segundo.
using System;
using System.Linq;
namespace duteis
{
public class uteis
{
private TimeSpan startingTime;
private TimeSpan endingTime;
private DayOfWeek[] excludeDays;
public uteis(TimeSpan? startingTime, TimeSpan? endingTime, DayOfWeek[] excludeDays)
{
this.startingTime = startingTime ?? new TimeSpan(8, 30, 0);
this.endingTime = endingTime ?? new TimeSpan(17, 30, 0);
this.excludeDays = excludeDays ?? new DayOfWeek[]
{
DayOfWeek.Saturday ,
DayOfWeek.Sunday
};
}
public uteis()
: this(null, null, null)
{
}
public double Calculate(DateTime startDate, DateTime endDate)
{
var counter = startDate;
double hours = 0;
while (counter <= endDate)
{
var dayStart = counter.Date.Add(startingTime);
var dayEnd = counter.Date.Add(endingTime);
var nextDayStart = startDate.Date.Add(startingTime).AddDays(1);
if (counter < dayStart)
counter = dayStart;
if (excludeDays == null ||
excludeDays.Contains(counter.DayOfWeek) == false)
{
if (endDate < nextDayStart)
{
var ticks = Math.Min(endDate.Ticks, dayEnd.Ticks) - counter.Ticks;
hours = TimeSpan.FromTicks(ticks).TotalHours;
break;
}
else if (counter.Date == startDate.Date)
{
if (counter >= dayStart && counter <= dayEnd)
{
hours += (dayEnd - counter).TotalHours;
}
}
else if (counter.Date == endDate.Date &&
startDate.Date != endDate.Date)
{
if (counter >= dayStart && counter <= dayEnd)
{
hours += (counter - dayStart).TotalHours;
}
else if (counter > dayEnd)
{
hours += (endingTime - startingTime).TotalHours;
}
}
else
{
hours += (endingTime - startingTime).TotalHours;
}
}
counter = counter.AddDays(1);
if (counter.Date == endDate.Date)
counter = endDate;
}
return hours;
}
}
}
Atenção a estas linhas onde se define a duração do dia ùtil, e são tratados os "excludeDays" que podem ser os feriados como escrevi anteriormente.
this.startingTime = startingTime ?? new TimeSpan(8, 30, 0);
this.endingTime = endingTime ?? new TimeSpan(17, 30, 0);
this.excludeDays = excludeDays ?? new DayOfWeek[]