我有一个要求,我需要在日期字段上工作,所以要求是这样的事情
我会将该字段称为最短可能日期
为日期添加+1
如果最小可能日期恰好在加入1天后的周末(周六或周日),则显示下一个工作日,即周一
如果最低可能日期恰好落在假期,请显示下一个工作日。 (假期1.1,1.5,3.10,25.12,26.12)
如果最小可能日期恰好在加入1天后的周末(周六或周日),以及之后的第二天是假日,则显示下一个工作日。例如:如果最小可能日是星期六,则在+1天之后,我们将必须在星期一显示。但如果星期一恰好是假日,那么我们必须在星期二展示。
我已经通过多个if和else案例尝试了上述问题的解决方案,但只是想知道是否有任何通用和优雅的方式来做它?
我试过了
var Holidays = new List<DateTime>();
Holidays.Add(new DateTime(DateTime.Now.Year,1,1));
Holidays.Add(new DateTime(DateTime.Now.Year,1,5));
Holidays.Add(new DateTime(DateTime.Now.Year,3,10));
Holidays.Add(new DateTime(DateTime.Now.Year,12,25));
if(date.DayOfWeek === DayOfWeek.Saturday || date.DayOfWeek === DayOfWeek.Sunday)
{
//Logic to add +1 and again some logic to check for weekends and weekdays
}
else if(holidays.Contain(date))
{
//Logic to add +1 and again some logic to check for weekends and weekdays
}
答案 0 :(得分:22)
基本上你想要下一个工作日。所以你可以循环这个条件,在当前日期增加1天
do {
date = date.AddDays(1);
} while(IsHolliday(date) || IsWeekEnd(date));
在上一段代码中,IsHolliday
是一个谓词,告诉日期是否为假日。例如,无耻地重复使用您的代码:
class Program
{
private static readonly HashSet<DateTime> Holidays = new HashSet<DateTime>();
private static bool IsHoliday(DateTime date)
{
return Holidays.Contains(date);
}
private static bool IsWeekEnd(DateTime date)
{
return date.DayOfWeek == DayOfWeek.Saturday
|| date.DayOfWeek == DayOfWeek.Sunday;
}
private static DateTime GetNextWorkingDay(DateTime date)
{
do
{
date = date.AddDays(1);
} while (IsHoliday(date) || IsWeekEnd(date));
return date;
}
static void Main(string[] args)
{
Holidays.Add(new DateTime(DateTime.Now.Year, 1, 1));
Holidays.Add(new DateTime(DateTime.Now.Year, 1, 5));
Holidays.Add(new DateTime(DateTime.Now.Year, 3, 10));
Holidays.Add(new DateTime(DateTime.Now.Year, 12, 25));
var dt = GetNextWorkingDay(DateTime.Parse(@"2015-10-31"));
Console.WriteLine(dt);
Console.ReadKey();
}
}
答案 1 :(得分:5)
固定的日期列表是表达假期的有限方式。
请注意,您的列表仅包含当前年份的日期,因此如果今天是2015年12月30日,那么下一个假期将是2016年1月1日 - 您将无法在列表中找到。
还要考虑许多假期不是每年都在同一天。它们通常与一周中的某一天相关,有时它们是由宗教日历决定的,或者是任意的。
更强大的系统应该处理各种不同类型的假期。这是一种可能的实现方式:
public abstract class Holiday
{
public abstract DateTime? GetDate(int year);
}
public class MonthDayBasedHoliday : Holiday
{
private readonly int _month;
private readonly int _day;
public MonthDayBasedHoliday(int month, int day)
{
_month = month;
_day = day;
}
public override DateTime? GetDate(int year)
{
return new DateTime(year, _month, _day);
}
}
public class DayOfWeekBasedHoliday : Holiday
{
private readonly int _occurrence;
private readonly DayOfWeek _dayOfWeek;
private readonly int _month;
public DayOfWeekBasedHoliday(int occurrence, DayOfWeek dayOfWeek, int month)
{
_occurrence = occurrence;
_dayOfWeek = dayOfWeek;
_month = month;
}
public override DateTime? GetDate(int year)
{
if (_occurrence <= 4)
{
DateTime dt = new DateTime(year, _month, 1);
int delta = (_dayOfWeek - dt.DayOfWeek + 7) % 7;
delta += 7 * (_occurrence - 1);
return dt.AddDays(delta);
}
else // last occurrence in month
{
int daysInMonth = DateTime.DaysInMonth(year, _month);
DateTime dt = new DateTime(year, _month, daysInMonth);
int delta = (dt.DayOfWeek - _dayOfWeek + 7) % 7;
return dt.AddDays(-delta);
}
}
}
public class FixedDateBasedHoliday : Holiday
{
private readonly IDictionary<int, DateTime> _dates;
public FixedDateBasedHoliday(params DateTime[] dates)
{
_dates = dates.ToDictionary(x => x.Year, x => x);
}
public override DateTime? GetDate(int year)
{
if (_dates.ContainsKey(year))
return _dates[year];
// fixed date not established for year
return null;
}
}
通过这些定义,我们现在可以更加健壮地定义假期,例如:
var holidays = new List<Holiday>();
// New Year's Day
holidays.Add(new MonthDayBasedHoliday(1, 1));
// President's Day (US)
holidays.Add(new DayOfWeekBasedHoliday(3, DayOfWeek.Monday, 2));
// Easter (Western Observance)
holidays.Add(new FixedDateBasedHoliday(new DateTime(2015, 4, 5), new DateTime(2016, 3, 27)));
// Memorial Day (US)
holidays.Add(new DayOfWeekBasedHoliday(5, DayOfWeek.Monday, 5));
// Christmas Day
holidays.Add(new MonthDayBasedHoliday(12, 25));
现在,我们可以按照您的要求创建一个检查下一个工作日的方法:
public static DateTime GetNextNonHolidayWeekDay(DateTime date, IList<Holiday> holidays, IList<DayOfWeek> weekendDays)
{
// always start with tomorrow, and truncate time to be safe
date = date.Date.AddDays(1);
// calculate holidays for both this year and next year
var holidayDates = holidays.Select(x => x.GetDate(date.Year))
.Union(holidays.Select(x => x.GetDate(date.Year + 1)))
.Where(x=> x != null)
.Select(x=> x.Value)
.OrderBy(x => x).ToArray();
// increment until we get a non-weekend and non-holiday date
while (true)
{
if (weekendDays.Contains(date.DayOfWeek) || holidayDates.Contains(date))
date = date.AddDays(1);
else
return date;
}
}
该方法可以在抽象的Holiday
类中进行,也可以在任何地方进行。
示例用法(holidays
的上述定义):
var weekendDays = new[] { DayOfWeek.Saturday, DayOfWeek.Sunday };
DateTime workDay = GetNextNonHolidayWeekDay(new DateTime(2015, 12, 31), holidays, weekendDays);
// returns 2016-01-04
但这仍然不是一个完整的解决方案。许多假期都有更复杂的计算规则。作为练习留给读者,尝试在美国感恩节假期的第二天实施一个源自Holiday
的课程。第一天总是在11月的第四个星期四,但第二天总是“十一月的第四个星期四之后的星期五”,而不仅仅是“十一月的第四个星期五”(参见2019年11月的一个例子事项)。
答案 2 :(得分:3)
根据@fjardon的答案,您可以使用我的项目Nager.Date,它包含不同国家/地区的周末逻辑,并包含90个国家/地区的公共假期。
<强>的NuGet 强>
PM> install-package Nager.Date
代码段
//usings
using Nager.Date;
using Nager.Date.Extensions;
//logic
var date = DateTime.Today; //Set start date
var countryCode = CountryCode.US; //Set country
do
{
date = date.AddDays(1);
} while (DateSystem.IsPublicHoliday(date, countryCode) || date.IsWeekend(countryCode));
答案 3 :(得分:0)
var Holidays = new List<DateTime>();
Holidays.Add(new DateTime(DateTime.Now.Year, 1, 1));
Holidays.Add(new DateTime(DateTime.Now.Year, 1, 5));
Holidays.Add(new DateTime(DateTime.Now.Year, 3, 10));
Holidays.Add(new DateTime(DateTime.Now.Year, 12, 25));
var exclude = new List<DayOfWeek> {DayOfWeek.Saturday, DayOfWeek.Sunday};
var targetDate = new DateTime(2015, 12, 24);
var myDate = Enumerable.Range(1, 30)
.Select( i => targetDate.AddDays(i) )
.First(a =>
!( exclude.Contains( a.DayOfWeek ) ||
Holidays.Contains(a)) );
答案 4 :(得分:-1)
我在项目中有类似的要求,并且我使用SQl Server进行了检索。 如果您可以在SQL Server中进行操作,我们将有一个非常简单的查询,该查询将返回下一个工作日。
DECLARE @dt datetime='20150113'
SELECT DATEADD(dd,CASE WHEN DATEDIFF(dd,0,@dt)%7 > 3 THEN 7-DATEDIFF(dd,0,@dt)%7 ELSE 1 END,@dt)
说明:
根据数据库,总是基准日期为“ 1900年1月1日”
19000101->星期一->-因此结果是THEN VALUE = 1天
19000102->星期二->-因此结果是THEN VALUE = 1天
19000103->星期三->-因此结果是THEN VALUE = 1天
19000104->星期四->-因此结果是THEN VALUE = 1天
19000105->星期五->-所以结果是7- @ number = 3天
19000106->星期六->-结果是7- @ number = 2天
19000107->星期日->-因此结果是7- @ number = 1天