ฉันกำลังรื้อฟื้นโพสต์นี้เพราะวันนี้ฉันต้องหาวิธียกเว้นไม่เพียง แต่วันเสาร์และอาทิตย์ในวันธรรมดาเท่านั้น โดยเฉพาะอย่างยิ่งฉันต้องจัดการกับวันหยุดต่างๆที่เป็นไปได้ ได้แก่ :
- วันหยุดไม่ตรงตามประเทศ (อย่างน้อยสำหรับประเทศตะวันตกเช่นมกราคม 01)
- วันหยุดที่คำนวณได้ (เช่นวันจันทร์อีสเตอร์และอีสเตอร์)
- วันหยุดเฉพาะประเทศ (เช่นวันปลดปล่อยอิตาลีหรือ ID4 ของสหรัฐอเมริกา)
- วันหยุดเฉพาะเมือง (เช่นวันนักบุญอุปถัมภ์ของกรุงโรม)
- วันหยุดอื่น ๆ ที่กำหนดเอง (เช่น "พรุ่งนี้สำนักงานของเราจะปิด")
ในที่สุดฉันก็ออกมาพร้อมกับชุดของคลาสตัวช่วย / ส่วนขยายต่อไปนี้แม้ว่าพวกเขาจะไม่สวยหรูอย่างโจ่งแจ้งเพราะพวกเขาใช้ลูปที่ไม่มีประสิทธิภาพเป็นจำนวนมาก แต่ก็เหมาะสมพอที่จะแก้ปัญหาของฉันได้ดี ฉันวางซอร์สโค้ดทั้งหมดไว้ที่นี่ในโพสต์นี้หวังว่าจะเป็นประโยชน์กับคนอื่นเช่นกัน
รหัสแหล่งที่มา
public static class DateTimeExtensions
{
public static int Years(DateTime dt1,DateTime dt2)
{
return Months(dt1,dt2)/12;
}
public static int Years2(DateTime start, DateTime end)
{
return (end.Year - start.Year - 1) +
(((end.Month > start.Month) ||
((end.Month == start.Month) && (end.Day >= start.Day))) ? 1 : 0);
}
public static int Months(DateTime dt1,DateTime dt2)
{
if (dt2<dt1)
{
DateTime dt0=dt1;
dt1=dt2;
dt2=dt0;
}
dt2=dt2.AddDays(-(dt1.Day-1));
return (dt2.Year-dt1.Year)*12+(dt2.Month-dt1.Month);
}
public static DateTime Max(DateTime dt1,DateTime dt2)
{
return (dt2>dt1?dt2:dt1);
}
public static DateTime Min(DateTime dt1,DateTime dt2)
{
return (dt2<dt1?dt2:dt1);
}
public static DateTime AddBusinessDays(
this DateTime current,
int days,
IEnumerable<DateTime> holidays = null)
{
var sign = Math.Sign(days);
var unsignedDays = Math.Abs(days);
for (var i = 0; i < unsignedDays; i++)
{
do
{
current = current.AddDays(sign);
}
while (current.DayOfWeek == DayOfWeek.Saturday
|| current.DayOfWeek == DayOfWeek.Sunday
|| (holidays != null && holidays.Contains(current.Date))
);
}
return current;
}
public static DateTime SubtractBusinessDays(
this DateTime current,
int days,
IEnumerable<DateTime> holidays)
{
return AddBusinessDays(current, -days, holidays);
}
public static int GetBusinessDays(
this DateTime startDate,
DateTime endDate,
IEnumerable<DateTime> holidays)
{
if (startDate > endDate)
throw new NotSupportedException("ERROR: [startDate] cannot be greater than [endDate].");
int cnt = 0;
for (var current = startDate; current < endDate; current = current.AddDays(1))
{
if (current.DayOfWeek == DayOfWeek.Saturday
|| current.DayOfWeek == DayOfWeek.Sunday
|| (holidays != null && holidays.Contains(current.Date))
)
{
}
else cnt++;
}
return cnt;
}
public static DateTime GetEasterSunday(int year)
{
int day = 0;
int month = 0;
int g = year % 19;
int c = year / 100;
int h = (c - (int)(c / 4) - (int)((8 * c + 13) / 25) + 19 * g + 15) % 30;
int i = h - (int)(h / 28) * (1 - (int)(h / 28) * (int)(29 / (h + 1)) * (int)((21 - g) / 11));
day = i - ((year + (int)(year / 4) + i + 2 - c + (int)(c / 4)) % 7) + 28;
month = 3;
if (day > 31)
{
month++;
day -= 31;
}
return new DateTime(year, month, day);
}
public static IEnumerable<DateTime> GetHolidays(IEnumerable<int> years, string countryCode = null, string cityName = null)
{
var lst = new List<DateTime>();
foreach (var year in years.Distinct())
{
lst.AddRange(new[] {
new DateTime(year, 1, 1),
new DateTime(year, 1, 6),
new DateTime(year, 5, 1),
new DateTime(year, 8, 15),
new DateTime(year, 11, 1),
new DateTime(year, 12, 8),
new DateTime(year, 12, 25),
new DateTime(year, 12, 26)
});
var easterDate = GetEasterSunday(year);
lst.Add(easterDate);
lst.Add(easterDate.AddDays(1));
if (!String.IsNullOrEmpty(countryCode))
{
switch (countryCode.ToUpper())
{
case "IT":
lst.Add(new DateTime(year, 4, 25));
break;
case "US":
lst.Add(new DateTime(year, 7, 4));
break;
case default:
break;
}
}
if (!String.IsNullOrEmpty(cityName))
{
switch (cityName)
{
case "Rome":
case "Roma":
lst.Add(new DateTime(year, 6, 29));
break;
case "Milano":
case "Milan":
lst.Add(new DateTime(year, 12, 7));
break;
default:
break;
}
}
}
return lst;
}
}
ข้อมูลการใช้งาน
โค้ดนี้ค่อนข้างอธิบายตัวเองได้ แต่นี่คือตัวอย่างสองสามตัวอย่างเพื่ออธิบายวิธีใช้งาน
เพิ่ม 10 วันทำการ (ข้ามเฉพาะวันเสาร์และวันอาทิตย์ของสัปดาห์)
var dtResult = DateTimeUtil.AddBusinessDays(srcDate, 10);
เพิ่ม 10 วันทำการ (ข้ามวันเสาร์วันอาทิตย์และวันหยุดประจำประเทศทั้งหมดสำหรับปี 2019)
var dtResult = DateTimeUtil.AddBusinessDays(srcDate, 10, GetHolidays(2019));
เพิ่ม 10 วันทำการ (ข้ามวันเสาร์วันอาทิตย์และวันหยุดอิตาลีทั้งหมดสำหรับปี 2019)
var dtResult = DateTimeUtil.AddBusinessDays(srcDate, 10, GetHolidays(2019, "IT"));
เพิ่ม 10 วันทำการ (ข้ามวันเสาร์วันอาทิตย์วันหยุดอิตาลีทั้งหมดและวันหยุดเฉพาะกรุงโรมสำหรับปี 2019)
var dtResult = DateTimeUtil.AddBusinessDays(srcDate, 10, GetHolidays(2019, "IT", "Rome"));
ตัวอย่างฟังก์ชันและโค้ดข้างต้นมีการอธิบายเพิ่มเติมในโพสต์ของบล็อกของฉัน