Saya menghidupkan kembali posting ini karena hari ini saya harus menemukan cara untuk mengecualikan tidak hanya hari kerja Sabtu dan Minggu tetapi juga hari libur. Lebih khusus lagi, saya perlu menangani berbagai rangkaian kemungkinan liburan, termasuk:
- hari libur negara-invarian (setidaknya untuk negara-negara Barat - seperti Januari, 01).
- hari libur yang dihitung (seperti Paskah dan Senin Paskah).
- hari libur khusus negara (seperti hari pembebasan Italia atau ID4 Amerika Serikat).
- hari libur khusus kota (seperti Roma St. Patron Day).
- hari libur khusus lainnya (seperti "besok kantor kami akan tutup").
Akhirnya, saya keluar dengan kumpulan kelas pembantu / ekstensi berikut: meskipun mereka tidak terlalu elegan, karena mereka membuat penggunaan loop yang tidak efisien secara masif, mereka cukup layak untuk menyelesaikan masalah saya untuk selamanya. Saya menjatuhkan seluruh kode sumber di sini, di posting ini, berharap itu akan berguna bagi orang lain juga.
Kode sumber
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;
}
}
Info penggunaan
Kode ini cukup jelas, namun berikut adalah beberapa contoh untuk menjelaskan bagaimana Anda dapat menggunakannya.
Tambahkan 10 hari kerja (hanya melewatkan hari Sabtu dan Minggu hari kerja)
var dtResult = DateTimeUtil.AddBusinessDays(srcDate, 10);
Tambahkan 10 hari kerja (melewatkan hari Sabtu, Minggu, dan semua hari libur nasional untuk tahun 2019)
var dtResult = DateTimeUtil.AddBusinessDays(srcDate, 10, GetHolidays(2019));
Tambahkan 10 hari kerja (melewatkan hari Sabtu, Minggu, dan semua hari libur Italia untuk tahun 2019)
var dtResult = DateTimeUtil.AddBusinessDays(srcDate, 10, GetHolidays(2019, "IT"));
Tambahkan 10 hari kerja (melewatkan hari Sabtu, Minggu, semua hari libur Italia, dan hari libur khusus Roma untuk 2019)
var dtResult = DateTimeUtil.AddBusinessDays(srcDate, 10, GetHolidays(2019, "IT", "Rome"));
Fungsi dan contoh kode di atas dijelaskan lebih lanjut dalam posting blog saya ini.