C#
 ASP.NET
 MVC
 Visual Studio 2019
 SLA
Here is my solution for calculating Service Level Agreement (SLA) dates.
Given a start date and time, and number of days, it will calculate an end date based on business hours only.
It takes into account weekends and public holidays (the list of public holidays is held in an in-memory cache).
It uses 2 TimeSpan objects that define what time the workday starts and ends.
public DateTime? CalculateSLADueDate(DateTime slaStartDateUTC, double slaDays, TimeSpan workdayStartUTC, TimeSpan workdayEndUTC)
{
if ((slaDays < 0)
|| (workdayStartUTC > workdayEndUTC))
{
return null;
}
var dueDate = slaStartDateUTC;
var tsWorkdayHours = (workdayEndUTC - workdayStartUTC);
var tsSlaCount = TimeSpan.FromHours(slaDays * ((workdayEndUTC - workdayStartUTC)).TotalHours);
//get list of public holidays from in-memory cache
var blPublicHoliday = new PublicHoliday();
IList publicHolidays = blPublicHoliday.SelectAll();
do
{
if ((dueDate.DayOfWeek == DayOfWeek.Saturday)
|| (dueDate.DayOfWeek == DayOfWeek.Sunday)
|| publicHolidays.Any(x => x.HolidayDate == dueDate.Date)
|| ((dueDate.TimeOfDay >= workdayEndUTC) && (dueDate.TimeOfDay < workdayStartUTC)))
{
//jump to start of next day
dueDate = dueDate.AddDays(1);
dueDate = new DateTime(dueDate.Year, dueDate.Month, dueDate.Day, workdayStartUTC.Hours, workdayStartUTC.Minutes, workdayStartUTC.Seconds);
}
else if ((dueDate.TimeOfDay == workdayStartUTC) && (tsSlaCount >= tsWorkdayHours))
{
//add a whole working day
dueDate = dueDate.AddDays(1);
tsSlaCount = tsSlaCount.Subtract(tsWorkdayHours);
}
else if (dueDate.TimeOfDay == workdayStartUTC)
{
//end day - add remainder of time for final work day
dueDate = dueDate.Add(tsSlaCount);
tsSlaCount = tsSlaCount.Subtract(tsSlaCount);
}
else
{
if(workdayEndUTC > dueDate.TimeOfDay)
{
//start day and still in business hours - add rest of today
tsSlaCount = tsSlaCount.Subtract(workdayEndUTC - dueDate.TimeOfDay);
dueDate = dueDate.Add(workdayEndUTC - dueDate.TimeOfDay);
}
if (tsSlaCount.Ticks > 0)
{
//if theres more to process - jump to start of next day
dueDate = dueDate.AddDays(1);
dueDate = new DateTime(dueDate.Year, dueDate.Month, dueDate.Day, workdayStartUTC.Hours, workdayStartUTC.Minutes, workdayStartUTC.Seconds);
}
}
}
while (tsSlaCount.Ticks > 0);
return dueDate;
}