C#-以天、小时和分钟计算sla,不包括非工作时间、周末和公共假日
我在搜索stackoverflow,以找到与C#中要解决的类似问题的精确匹配问题和响应 虽然我发现这些问题有一些相似之处,但我找不到任何关于如何以天、小时和分钟为单位计算sla(不包括公共假日、周末和非工作时间)的问题和回答 例如,我已将票证的datetime提升为2019年2月21日10:00:00 pm,如果我只想添加n(在本例中为21)个工作小时数,不包括非工作时间、周末和公共假日,则以C#查找该票证的sla datetime 虽然我已经实现了一些只计算工作时间、周末的逻辑,但发现很难排除公共假日。也要欣赏比长行函数更好、更简单、更容易理解的方法(可能使用linq)。感谢来自社区的任何示例代码 我已经从其他stackoverflow链接中得到了一个改进的工作解决方案,如下所示,但这需要进一步改进,以简化和解决任何可能出现的错误,例如,如果我们连续休假2天,然后从第3天开始计算sla,则此场景无法处理,等等 到目前为止,我得到的解决方案是:C#-以天、小时和分钟计算sla,不包括非工作时间、周末和公共假日,c#,.net,linq,C#,.net,Linq,我在搜索stackoverflow,以找到与C#中要解决的类似问题的精确匹配问题和响应 虽然我发现这些问题有一些相似之处,但我找不到任何关于如何以天、小时和分钟为单位计算sla(不包括公共假日、周末和非工作时间)的问题和回答 例如,我已将票证的datetime提升为2019年2月21日10:00:00 pm,如果我只想添加n(在本例中为21)个工作小时数,不包括非工作时间、周末和公共假日,则以C#查找该票证的sla datetime 虽然我已经实现了一些只计算工作时间、周末的逻辑,但发现很难排除
public virtual DateTime AddWithinWorkingHours(DateTime start, TimeSpan offset)
{
//Get publicholidaysList from holiday table to not to include in working hour calculation
var holidaysList = _holidayManager.GetHolidays().Result;
// Don't start counting hours until start time is during working hours
if (start.TimeOfDay.TotalHours > StartHour + HoursPerDay)
start = start.Date.AddDays(1).AddHours(StartHour);
if (start.TimeOfDay.TotalHours < StartHour)
start = start.Date.AddHours(StartHour);
if (start.DayOfWeek == DayOfWeek.Saturday)
start.AddDays(2);
//if it is a Sunday or holiday date, skip that date in workinghour calc
else if (start.DayOfWeek == DayOfWeek.Sunday || holidaysList.Exists(hd=>hd.Date == start.Date))
start.AddDays(1);
// Calculate how much working time already passed on the first day
TimeSpan firstDayOffset = start.TimeOfDay.Subtract(TimeSpan.FromHours(StartHour));
// Calculate number of whole days to add
int wholeDays = (int)(offset.Add(firstDayOffset).TotalHours / HoursPerDay);
// How many hours off the specified offset does this many whole days consume?
TimeSpan wholeDaysHours = TimeSpan.FromHours(wholeDays * HoursPerDay);
// Calculate the final time of day based on the number of whole days spanned and the specified offset
TimeSpan remainder = offset - wholeDaysHours;
// How far into the week is the starting date?
int weekOffset = ((int)(start.DayOfWeek + 7) - (int)DayOfWeek.Monday) % 7;
// How many weekends are spanned?
int weekends = (int)((wholeDays + weekOffset) / 5);
// Calculate the final result using all the above calculated values
return start.AddDays(wholeDays + weekends * 2).Add(remainder);
}
public virtual DateTime addwithin workinghours(DateTime start,TimeSpan offset)
{
//从假日表中获取PublicHolidays列表,以便不包括在工作时间计算中
var holidaysList=\u holidayManager.GetHolidays().Result;
//直到开始时间在工作时间内,才开始计算小时数
如果(start.TimeOfDay.TotalHours>StartHour+HoursPerDay)
开始=开始.Date.AddDays(1).AddHours(StartHour);
if(开始时间:一天的时间:总小时数<开始时间)
开始=开始.Date.AddHours(StartHour);
如果(start.DayOfWeek==DayOfWeek.Saturday)
开始。添加天数(2);
//如果是周日或假日日期,请在workinghour calc中跳过该日期
else如果(start.DayOfWeek==DayOfWeek.Sunday | | holidaysList.Exists(hd=>hd.Date==start.Date))
开始。添加天数(1);
//计算第一天已经过了多少工作时间
TimeSpan firstDayOffset=start.TimeOfDay.Subtract(TimeSpan.FromHours(StartHour));
//计算要添加的总天数
int wholeDays=(int)(offset.Add(firstDayOffset.TotalHours/HoursPerDay);
//这一整天要消耗多少小时的指定偏移量?
TimeSpan wholeDaysHours=TimeSpan.FromHours(wholeDays*Hours perday);
//根据跨越的天数和指定的偏移量计算一天中的最后时间
TimeSpan余数=偏移量-总天数;
//开始日期是多久?
int weekOffset=((int)(start.DayOfWeek+7)-(int)DayOfWeek.Monday)%7;
//有多少个周末?
int周末=(int)((全天+周末)/5);
//使用上述所有计算值计算最终结果
返回start.AddDays(全天+周末*2).Add(剩余);
}
我实际上花了最后一个小时来实施这个解决方案,它结合了另一个stackoverflow问题()中的一个问题,该问题计算某个日期的工作时间+一块金块,根据指定的国家验证某个日期是否为假日。
首先安装熔核
PM> install-package Nager.Date
然后,我创建了3种方法来实现您的功能,但它很简单,您可以对其进行优化,以采用CountryCode以及一个工作日的小时数和开始时间,但我将其硬编码仅用于示例目的:
private static DateTime AddWithinWorkingHours(DateTime start, TimeSpan offset)
{
const int hoursPerDay = 8;
const int startHour = 9;
// Don't start counting hours until start time is during working hours
if (start.TimeOfDay.TotalHours > startHour + hoursPerDay)
start = start.Date.AddDays(1).AddHours(startHour);
if (start.TimeOfDay.TotalHours < startHour)
start = start.Date.AddHours(startHour);
start = CheckTillNoLongerHoliday(start);
if (start.DayOfWeek == DayOfWeek.Saturday)
start = start.AddDays(2);
else if (start.DayOfWeek == DayOfWeek.Sunday)
start = start.AddDays(1);
//Saving this proccessed date to check later if there are more holidays
var dateAfterArranges = start;
// Calculate how much working time already passed on the first day
TimeSpan firstDayOffset = start.TimeOfDay.Subtract(TimeSpan.FromHours(startHour));
// Calculate number of whole days to add
int wholeDays = (int)(offset.Add(firstDayOffset).TotalHours / hoursPerDay);
// How many hours off the specified offset does this many whole days consume?
TimeSpan wholeDaysHours = TimeSpan.FromHours(wholeDays * hoursPerDay);
// Calculate the final time of day based on the number of whole days spanned and the specified offset
TimeSpan remainder = offset - wholeDaysHours;
// How far into the week is the starting date?
int weekOffset = ((int)(start.DayOfWeek + 7) - (int)DayOfWeek.Monday) % 7;
// How many weekends are spanned?
int weekends = (int)((wholeDays + weekOffset) / 5);
//Get the final date without the holidays
start = start.AddDays(wholeDays + weekends * 2).Add(remainder);
//Check again if in this timeSpan there were any more holidays
return InPeriodCheckHolidaysOnWorkingDays(dateAfterArranges, start);
}
private static DateTime CheckTillNoLongerHoliday(DateTime date)
{
if (DateSystem.IsPublicHoliday(date, CountryCode.PT) && !DateSystem.IsWeekend(date, CountryCode.PT))
{
date = date.AddDays(1);
date = CheckTillNoLongerHoliday(date);
}
return date;
}
private static DateTime InPeriodCheckHolidaysOnWorkingDays(DateTime start, DateTime end)
{
var publicHolidays = DateSystem.GetPublicHoliday(2019, CountryCode.PT);
var holidaysSpent = publicHolidays.Where(x => x.Date.Date >= start.Date && x.Date.Date < end.Date);
foreach (var holiday in holidaysSpent)
{
if (!DateSystem.IsWeekend(holiday.Date, CountryCode.PT))
{
end = end.AddDays(1);
if (DateSystem.IsWeekend(end, CountryCode.PT))
{
end = end.AddDays(2);
}
}
}
return end;
}
private static DateTime addwithin workinghours(DateTime start,TimeSpan offset)
{
每小时常数=8;
常数int startHour=9;
//直到开始时间在工作时间内,才开始计算小时数
如果(start.TimeOfDay.TotalHours>startHour+hoursPerDay)
开始=开始.Date.AddDays(1).AddHours(startHour);
if(开始时间:一天的时间:总小时数<开始时间)
开始=开始.Date.AddHours(startHour);
开始=CheckTillNoLongerHoliday(开始);
如果(start.DayOfWeek==DayOfWeek.Saturday)
开始=开始。添加天数(2);
else if(start.DayOfWeek==DayOfWeek.Sunday)
开始=开始。添加天数(1);
//保存此已处理日期,以便稍后检查是否有更多假期
var dateAfterArranges=开始;
//计算第一天已经过了多少工作时间
TimeSpan firstDayOffset=start.TimeOfDay.Subtract(TimeSpan.FromHours(startHour));
//计算要添加的总天数
int wholeDays=(int)(offset.Add(firstDayOffset.TotalHours/hoursPerDay);
//这一整天要消耗多少小时的指定偏移量?
TimeSpan wholeDaysHours=TimeSpan.FromHours(wholeDays*Hours perday);
//根据跨越的天数和指定的偏移量计算一天中的最后时间
TimeSpan余数=偏移量-总天数;
//开始日期是多久?
int weekOffset=((int)(start.DayOfWeek+7)-(int)DayOfWeek.Monday)%7;
//有多少个周末?
int周末=(int)((全天+周末)/5);
//得到最后的日期,没有假期
开始=开始.AddDays(全天+周末*2).添加(剩余);
//再次检查此时间段内是否还有其他假期
在工作日内的定期检查日返回(安排日期后,开始);
}
专用静态日期时间检查TillNoLongerHoliday(日期时间日期)
{
如果(日期)
public void CalendarDateAddSample()
{
CalendarDateAdd calendarDateAdd = new CalendarDateAdd();
// weekdays
calendarDateAdd.AddWorkingWeekDays();
// holidays
calendarDateAdd.ExcludePeriods.Add( new Day( 2011, 4, 5, calendarDateAdd.Calendar ) );
// working hours
calendarDateAdd.WorkingHours.Add( new HourRange( new Time( 08, 30 ), new Time( 12 ) ) );
calendarDateAdd.WorkingHours.Add( new HourRange( new Time( 13, 30 ), new Time( 18 ) ) );
DateTime start = new DateTime( 2011, 4, 1, 9, 0, 0 );
TimeSpan offset = new TimeSpan( 22, 0, 0 ); // 22 hours
DateTime? end = calendarDateAdd.Add( start, offset );
Console.WriteLine( "start: {0}", start );
// > start: 01.04.2011 09:00:00
Console.WriteLine( "offset: {0}", offset );
// > offset: 22:00:00
Console.WriteLine( "end: {0}", end );
// > end: 06.04.2011 16:30:00
}
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<BusObj.PublicHoliday> 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;
}
public static class DatetimeExtensionMethod
{
public static TimeSpan LocalTimeSpanToUTC(this TimeSpan ts)
{
DateTime dt = DateTime.Now.Date.Add(ts);
DateTime dtUtc = dt.ToUniversalTime();
TimeSpan tsUtc = dtUtc.TimeOfDay;
return tsUtc;
}
public static TimeSpan UTCTimeSpanToLocal(this TimeSpan tsUtc)
{
DateTime dtUtc = DateTime.UtcNow.Date.Add(tsUtc);
DateTime dt = dtUtc.ToLocalTime();
TimeSpan ts = dt.TimeOfDay;
return ts;
}
}