Saya pernah memiliki tugas seperti itu sebelumnya dan saya punya solusinya. Saya akan menghindari menghitung semua hari di antara saat itu dapat dihindari, yang terjadi di sini. Saya bahkan tidak menyebutkan membuat banyak contoh DateTime, seperti yang saya lihat di salah satu jawaban di atas. Ini benar-benar pemborosan daya pemrosesan. Apalagi dalam situasi dunia nyata, ketika Anda harus memeriksa interval waktu beberapa bulan. Lihat kode saya, dengan komentar, di bawah.
///<summary>/// Calculates number of business days, taking into account:/// - weekends (Saturdays and Sundays)/// - bank holidays in the middle of the week///</summary>///<param name="firstDay">First day in the time interval</param>///<param name="lastDay">Last day in the time interval</param>///<param name="bankHolidays">List of bank holidays excluding weekends</param>///<returns>Number of business days during the 'span'</returns>publicstaticintBusinessDaysUntil(this DateTime firstDay, DateTime lastDay, params DateTime[] bankHolidays)
{
firstDay = firstDay.Date;
lastDay = lastDay.Date;
if (firstDay > lastDay)
thrownew ArgumentException("Incorrect last day " + lastDay);
TimeSpan span = lastDay - firstDay;
int businessDays = span.Days + 1;
int fullWeekCount = businessDays / 7;
// find out if there are weekends during the time exceedng the full weeksif (businessDays > fullWeekCount*7)
{
// we are here to find out if there is a 1-day or 2-days weekend// in the time interval remaining after subtracting the complete weeksint firstDayOfWeek = (int) firstDay.DayOfWeek;
int lastDayOfWeek = (int) lastDay.DayOfWeek;
if (lastDayOfWeek < firstDayOfWeek)
lastDayOfWeek += 7;
if (firstDayOfWeek <= 6)
{
if (lastDayOfWeek >= 7)// Both Saturday and Sunday are in the remaining time interval
businessDays -= 2;
elseif (lastDayOfWeek >= 6)// Only Saturday is in the remaining time interval
businessDays -= 1;
}
elseif (firstDayOfWeek <= 7 && lastDayOfWeek >= 7)// Only Sunday is in the remaining time interval
businessDays -= 1;
}
// subtract the weekends during the full weeks in the interval
businessDays -= fullWeekCount + fullWeekCount;
// subtract the number of bank holidays during the time intervalforeach (DateTime bankHoliday in bankHolidays)
{
DateTime bh = bankHoliday.Date;
if (firstDay <= bh && bh <= lastDay)
--businessDays;
}
return businessDays;
}
Diedit oleh Slauma, Agustus 2011
Jawaban yang bagus! Namun ada sedikit bug. Saya mengambil kebebasan untuk mengedit jawaban ini karena penjawab tidak ada sejak 2009.
Kode di atas mengasumsikan bahwa DayOfWeek.Sundaymemiliki nilai 7yang tidak demikian. Nilainya sebenarnya 0. Ini mengarah pada perhitungan yang salah jika misalnya firstDaydan lastDaykeduanya adalah hari Minggu yang sama. Metode ini kembali 1dalam kasus ini tetapi seharusnya begitu 0.
Perbaikan termudah untuk bug ini: Ganti kode di atas baris di mana firstDayOfWeekdan lastDayOfWeekdideklarasikan sebagai berikut:
int firstDayOfWeek = firstDay.DayOfWeek == DayOfWeek.Sunday
? 7 : (int)firstDay.DayOfWeek;
int lastDayOfWeek = lastDay.DayOfWeek == DayOfWeek.Sunday
? 7 : (int)lastDay.DayOfWeek;
+1 Itu mungkin cara termudah dan paling efisien untuk melakukannya (solusi saya yang berasal dari C ++ tidak menggunakan dukungan TimeSpan, C # membuat beberapa tugas jadi lebih mudah). BankHolidays juga merupakan sentuhan yang bagus!
RedGlyph
2
Pastikan juga hari libur bank sebagai berikut: if (firstDay <= bh && bh <= lastDay && bh.IsWorkingDay ())
Tawani
5
Terima kasih untuk metodenya. Meskipun, saya harus menambahkan yang berikut ini ke substraksi / iterasi hari libur bank jika-pernyataan:, jika tidak && !(bh.DayOfWeek == DayOfWeek.Sunday || bh.DayOfWeek == DayOfWeek.Saturday), itu akan mengurangi hari yang sama dua kali, jika hari libur jatuh di akhir pekan.
KristianB
Saya mengubah loop terakhir untuk pernyataan Linq: businessDays - = bankHolidays.Select (bankHoliday => bankHoliday.Date) .Count (bh => firstDay <= bh && bh <= lastDay);
Kerja bagus, tapi mungkin menggunakan enum DayOfWeek itu sendiri daripada melemparkannya ke int?
Neo
3
Serius, solusi terbaik di luar sana. Cheers Alec
Mizmor
6
Perhatikan bahwa meskipun fungsi ini mengembalikan ganda, seharusnya hanya dipercaya untuk memberikan seluruh hari kerja. Itu tidak mengembalikan jawaban yang benar untuk hari-hari pecahan ketika waktu terlibat.
Pakman
4
Sekadar komentar, dengan '1+' diasumsikan mulai dari hari pertama hingga akhir hari terakhir, tanpa '1+' itu mengasumsikan akhir hari pertama hingga akhir hari terakhir. Butuh beberapa saat bagi saya untuk mengetahuinya, karena saya berasumsi mulai dari hari pertama hingga awal hari terakhir, yang lebih masuk akal bagi saya.
Jeffry van de Vuurst
11
Ini BUKAN jawaban yang benar. Hari bisa keluar hingga 4. Hampir benar, tidak memperhitungkan kapan hari awal dan akhir berakhir di sekitar akhir pekan, yang merupakan bagian tersulit. Awal - akhir juga tidak boleh berada di dalam tanda kurung. Ini tidak ada hubungannya dengan masalah. 60% dari waktu solusi ini SALAH .
Burung Hantu
47
Saya tahu pertanyaan ini sudah terpecahkan, tetapi saya pikir saya bisa memberikan jawaban yang lebih lugas yang dapat membantu pengunjung lain di masa depan.
Jauh lebih jelas, dan solusi yang disebutkan memungkinkan untuk menghilangkan hari libur bank. Mereka jauh lebih lambat dalam jumlah besar; Dalam LINQPad menghitung hari kerja untuk 90 hari kesenjangan dalam 1 juta loop iterasi membutuhkan waktu 10 detik menggunakan solusi ini, dan hanya sekitar 0,2 detik menggunakan jawaban yang diterima atau jawaban Alec Pojidaev yang jauh lebih bagus.
Whelkaholism
Untuk menjadi inklusif, kodenya harus: return Enumerable .Range (0, dayDifference + 1) ...
Edza
tidak mengembalikan hari di masa lalu. Seperti -18 hari kerja.
iwtu
@ iwtu Ini mengasumsikan bahwa to > from. Mungkin itu masalahnya?
Alpha
22
Tentukan Metode Ekstensi pada DateTime seperti ini:
Kemudian, gunakan dalam klausa Di mana untuk memfilter daftar tanggal yang lebih luas:
var allDates = GetDates(); // method which returns a list of dates// filter dates by working day's var countOfWorkDays = allDates
.Where(day => day.IsWorkingDay())
.Count() ;
Apakah Anda tidak akan hanya melanjutkan dan memperpanjang jangka waktu juga sehingga Anda dapat menggunakannya - karena dia mengatakan ingin menggunakan jarak antara dua tanggal dan bukan daftar tanggal?
WesleyJohnson
Jarak antara dua tanggal adalah jumlah hari antara, jadi Hitungan () cukup.
Carles Company
3
Saya tidak yakin mengapa ini adalah jawaban yang cocok ... dia tidak memiliki daftar hari individu, dia memiliki dua tanggal dan dia ingin menemukan jumlah hari kerja di antaranya. Untuk menggunakan solusi ini, Anda harus menyediakan fungsi lain yang menghasilkan daftar setiap tanggal di antara twyp.
Adam Robinson
1
adam, ini adalah contoh sederhana dengan jumlah kode minimum yang diperlukan untuk mendemonstrasikan sebuah konsep. Dalam jawaban asli saya, saya juga menyertakan loop yang memunculkan daftar allDates yang telah saya abstraksi menjadi fungsi "GetDates". Tes IsWorkingDay dapat dengan mudah dipindahkan dari pernyataan LINQ dan ke loop itu. Saya pribadi suka bagaimana sekarang karena sangat mudah dibaca manusia tentang apa yang terjadi.
Qwerty
10
Bisa dipersingkat dengan mengubah Where to Count, dan menghilangkan Count
recursive
12
Saya menggunakan kode berikut untuk juga memperhitungkan hari libur bank:
publicclassWorkingDays
{
public List<DateTime> GetHolidays()
{
var client = new WebClient();
var json = client.DownloadString("https://www.gov.uk/bank-holidays.json");
var js = new JavaScriptSerializer();
var holidays = js.Deserialize <Dictionary<string, Holidays>>(json);
return holidays["england-and-wales"].events.Select(d => d.date).ToList();
}
publicintGetWorkingDays(DateTime from, DateTime to)
{
var totalDays = 0;
var holidays = GetHolidays();
for (var date = from.AddDays(1); date <= to; date = date.AddDays(1))
{
if (date.DayOfWeek != DayOfWeek.Saturday
&& date.DayOfWeek != DayOfWeek.Sunday
&& !holidays.Contains(date))
totalDays++;
}
return totalDays;
}
}
publicclassHolidays
{
publicstring division { get; set; }
public List<Event> events { get; set; }
}
publicclassEvent
{
public DateTime date { get; set; }
publicstring notes { get; set; }
publicstring title { get; set; }
}
Dan Tes Unit:
[TestClass]
publicclassWorkingDays
{
[TestMethod]
publicvoidSameDayIsZero()
{
var service = new WorkingDays();
varfrom = new DateTime(2013, 8, 12);
Assert.AreEqual(0, service.GetWorkingDays(from, from));
}
[TestMethod]
publicvoidCalculateDaysInWorkingWeek()
{
var service = new WorkingDays();
varfrom = new DateTime(2013, 8, 12);
var to = new DateTime(2013, 8, 16);
Assert.AreEqual(4, service.GetWorkingDays(from, to), "Mon - Fri = 4");
Assert.AreEqual(1, service.GetWorkingDays(from, new DateTime(2013, 8, 13)), "Mon - Tues = 1");
}
[TestMethod]
publicvoidNotIncludeWeekends()
{
var service = new WorkingDays();
varfrom = new DateTime(2013, 8, 9);
var to = new DateTime(2013, 8, 16);
Assert.AreEqual(5, service.GetWorkingDays(from, to), "Fri - Fri = 5");
Assert.AreEqual(2, service.GetWorkingDays(from, new DateTime(2013, 8, 13)), "Fri - Tues = 2");
Assert.AreEqual(1, service.GetWorkingDays(from, new DateTime(2013, 8, 12)), "Fri - Mon = 1");
}
[TestMethod]
publicvoidAccountForHolidays()
{
var service = new WorkingDays();
varfrom = new DateTime(2013, 8, 23);
Assert.AreEqual(0, service.GetWorkingDays(from, new DateTime(2013, 8, 26)), "Fri - Mon = 0");
Assert.AreEqual(1, service.GetWorkingDays(from, new DateTime(2013, 8, 27)), "Fri - Tues = 1");
}
}
mengapa Anda mulai menghitung dengan menambahkan 1 hari ke "from" @ for (var date = from.AddDays (1); date <= to; date = date.AddDays (1))?
Oncel Umut TURER
6
Nah ini telah dipukuli sampai mati. :) Namun saya tetap akan memberikan jawaban lain karena saya membutuhkan sesuatu yang sedikit berbeda. Solusi ini berbeda karena mengembalikan Business TimeSpan antara awal dan akhir, dan Anda dapat mengatur jam kerja hari itu, serta menambahkan hari libur. Jadi, Anda dapat menggunakannya untuk menghitung apakah itu terjadi dalam satu hari, di sepanjang hari, selama akhir pekan, dan bahkan hari libur. Dan Anda bisa mendapatkan hari kerja atau tidak dengan hanya mendapatkan apa yang Anda butuhkan dari objek TimeSpan yang dikembalikan. Dan cara menggunakan daftar hari, Anda dapat melihat betapa mudahnya menambahkan daftar hari non-kerja jika bukan Sab dan Ming yang biasa. Dan saya mengujinya selama setahun, dan tampaknya super cepat.
Saya hanya berharap kode yang ditempelkan akurat. Tapi saya tahu itu berhasil.
publicstatic TimeSpan GetBusinessTimespanBetween(
DateTime start, DateTime end,
TimeSpan workdayStartTime, TimeSpan workdayEndTime,
List<DateTime> holidays = null)
{
if (end < start)
thrownew ArgumentException("start datetime must be before end datetime.");
// Just create an empty list for easier coding.if (holidays == null) holidays = new List<DateTime>();
if (holidays.Where(x => x.TimeOfDay.Ticks > 0).Any())
thrownew ArgumentException("holidays can not have a TimeOfDay, only the Date.");
var nonWorkDays = new List<DayOfWeek>() { DayOfWeek.Saturday, DayOfWeek.Sunday };
var startTime = start.TimeOfDay;
// If the start time is before the starting hours, set it to the starting hour.if (startTime < workdayStartTime) startTime = workdayStartTime;
var timeBeforeEndOfWorkDay = workdayEndTime - startTime;
// If it's after the end of the day, then this time lapse doesn't count.if (timeBeforeEndOfWorkDay.TotalSeconds < 0) timeBeforeEndOfWorkDay = new TimeSpan();
// If start is during a non work day, it doesn't count.if (nonWorkDays.Contains(start.DayOfWeek)) timeBeforeEndOfWorkDay = new TimeSpan();
elseif (holidays.Contains(start.Date)) timeBeforeEndOfWorkDay = new TimeSpan();
var endTime = end.TimeOfDay;
// If the end time is after the ending hours, set it to the ending hour.if (endTime > workdayEndTime) endTime = workdayEndTime;
var timeAfterStartOfWorkDay = endTime - workdayStartTime;
// If it's before the start of the day, then this time lapse doesn't count.if (timeAfterStartOfWorkDay.TotalSeconds < 0) timeAfterStartOfWorkDay = new TimeSpan();
// If end is during a non work day, it doesn't count.if (nonWorkDays.Contains(end.DayOfWeek)) timeAfterStartOfWorkDay = new TimeSpan();
elseif (holidays.Contains(end.Date)) timeAfterStartOfWorkDay = new TimeSpan();
// Easy scenario if the times are during the day day.if (start.Date.CompareTo(end.Date) == 0)
{
if (nonWorkDays.Contains(start.DayOfWeek)) returnnew TimeSpan();
elseif (holidays.Contains(start.Date)) returnnew TimeSpan();
return endTime - startTime;
}
else
{
var timeBetween = end - start;
var daysBetween = (int)Math.Floor(timeBetween.TotalDays);
var dailyWorkSeconds = (int)Math.Floor((workdayEndTime - workdayStartTime).TotalSeconds);
var businessDaysBetween = 0;
// Now the fun begins with calculating the actual Business days.if (daysBetween > 0)
{
var nextStartDay = start.AddDays(1).Date;
var dayBeforeEnd = end.AddDays(-1).Date;
for (DateTime d = nextStartDay; d <= dayBeforeEnd; d = d.AddDays(1))
{
if (nonWorkDays.Contains(d.DayOfWeek)) continue;
elseif (holidays.Contains(d.Date)) continue;
businessDaysBetween++;
}
}
var dailyWorkSecondsToAdd = dailyWorkSeconds * businessDaysBetween;
var output = timeBeforeEndOfWorkDay + timeAfterStartOfWorkDay;
output = output + new TimeSpan(0, 0, dailyWorkSecondsToAdd);
return output;
}
}
Dan berikut ini kode pengujiannya: Perhatikan bahwa Anda hanya perlu meletakkan fungsi ini di kelas yang disebut DateHelper agar kode pengujian berfungsi.
[TestMethod]
publicvoidTestGetBusinessTimespanBetween()
{
var workdayStart = new TimeSpan(8, 0, 0);
var workdayEnd = new TimeSpan(17, 0, 0);
var holidays = new List<DateTime>()
{
new DateTime(2018, 1, 15), // a Mondaynew DateTime(2018, 2, 15) // a Thursday
};
var testdata = new[]
{
new
{
expectedMinutes = 0,
start = new DateTime(2016, 10, 19, 9, 50, 0),
end = new DateTime(2016, 10, 19, 9, 50, 0)
},
new
{
expectedMinutes = 10,
start = new DateTime(2016, 10, 19, 9, 50, 0),
end = new DateTime(2016, 10, 19, 10, 0, 0)
},
new
{
expectedMinutes = 5,
start = new DateTime(2016, 10, 19, 7, 50, 0),
end = new DateTime(2016, 10, 19, 8, 5, 0)
},
new
{
expectedMinutes = 5,
start = new DateTime(2016, 10, 19, 16, 55, 0),
end = new DateTime(2016, 10, 19, 17, 5, 0)
},
new
{
expectedMinutes = 15,
start = new DateTime(2016, 10, 19, 16, 50, 0),
end = new DateTime(2016, 10, 20, 8, 5, 0)
},
new
{
expectedMinutes = 10,
start = new DateTime(2016, 10, 19, 16, 50, 0),
end = new DateTime(2016, 10, 20, 7, 55, 0)
},
new
{
expectedMinutes = 5,
start = new DateTime(2016, 10, 19, 17, 10, 0),
end = new DateTime(2016, 10, 20, 8, 5, 0)
},
new
{
expectedMinutes = 0,
start = new DateTime(2016, 10, 19, 17, 10, 0),
end = new DateTime(2016, 10, 20, 7, 5, 0)
},
new
{
expectedMinutes = 545,
start = new DateTime(2016, 10, 19, 12, 10, 0),
end = new DateTime(2016, 10, 20, 12, 15, 0)
},
// Spanning multiple weekdaysnew
{
expectedMinutes = 835,
start = new DateTime(2016, 10, 19, 12, 10, 0),
end = new DateTime(2016, 10, 21, 8, 5, 0)
},
// Spanning multiple weekdaysnew
{
expectedMinutes = 1375,
start = new DateTime(2016, 10, 18, 12, 10, 0),
end = new DateTime(2016, 10, 21, 8, 5, 0)
},
// Spanning from a Thursday to a Tuesday, 5 mins short of complete day.new
{
expectedMinutes = 1615,
start = new DateTime(2016, 10, 20, 12, 10, 0),
end = new DateTime(2016, 10, 25, 12, 5, 0)
},
// Spanning from a Thursday to a Tuesday, 5 mins beyond complete day.new
{
expectedMinutes = 1625,
start = new DateTime(2016, 10, 20, 12, 10, 0),
end = new DateTime(2016, 10, 25, 12, 15, 0)
},
// Spanning from a Friday to a Monday, 5 mins beyond complete day.new
{
expectedMinutes = 545,
start = new DateTime(2016, 10, 21, 12, 10, 0),
end = new DateTime(2016, 10, 24, 12, 15, 0)
},
// Spanning from a Friday to a Monday, 5 mins short complete day.new
{
expectedMinutes = 535,
start = new DateTime(2016, 10, 21, 12, 10, 0),
end = new DateTime(2016, 10, 24, 12, 5, 0)
},
// Spanning from a Saturday to a Monday, 5 mins short complete day.new
{
expectedMinutes = 245,
start = new DateTime(2016, 10, 22, 12, 10, 0),
end = new DateTime(2016, 10, 24, 12, 5, 0)
},
// Spanning from a Saturday to a Sunday, 5 mins beyond complete day.new
{
expectedMinutes = 0,
start = new DateTime(2016, 10, 22, 12, 10, 0),
end = new DateTime(2016, 10, 23, 12, 15, 0)
},
// Times within the same Saturday.new
{
expectedMinutes = 0,
start = new DateTime(2016, 10, 22, 12, 10, 0),
end = new DateTime(2016, 10, 23, 12, 15, 0)
},
// Spanning from a Saturday to the Sunday next week.new
{
expectedMinutes = 2700,
start = new DateTime(2016, 10, 22, 12, 10, 0),
end = new DateTime(2016, 10, 30, 12, 15, 0)
},
// Spanning a year.new
{
expectedMinutes = 143355,
start = new DateTime(2016, 10, 22, 12, 10, 0),
end = new DateTime(2017, 10, 30, 12, 15, 0)
},
// Spanning a year with 2 holidays.new
{
expectedMinutes = 142815,
start = new DateTime(2017, 10, 22, 12, 10, 0),
end = new DateTime(2018, 10, 30, 12, 15, 0)
},
};
foreach (var item in testdata)
{
Assert.AreEqual(item.expectedMinutes,
DateHelper.GetBusinessTimespanBetween(
item.start, item.end,
workdayStart, workdayEnd,
holidays)
.TotalMinutes);
}
}
Solusi ini menghindari iterasi, bekerja untuk perbedaan hari kerja + ve dan -ve dan menyertakan rangkaian pengujian unit untuk meregresi terhadap metode penghitungan hari kerja yang lebih lambat. Saya juga menyertakan metode ringkas untuk menambahkan hari kerja yang juga berfungsi dengan cara non-iteratif yang sama.
Pengujian unit mencakup beberapa ribu kombinasi tanggal untuk menguji secara menyeluruh semua kombinasi awal / akhir hari kerja dengan rentang tanggal kecil dan besar.
Penting : Kami membuat asumsi bahwa kami menghitung hari dengan mengecualikan tanggal mulai dan termasuk tanggal akhir. Ini penting saat menghitung hari kerja karena hari mulai / akhir spesifik yang Anda sertakan / kecualikan memengaruhi hasil. Ini juga memastikan bahwa perbedaan antara dua hari yang sama selalu nol dan bahwa kami hanya menyertakan hari kerja penuh karena biasanya Anda ingin jawabannya benar kapan saja pada tanggal mulai saat ini (seringkali hari ini) dan menyertakan tanggal akhir lengkap (mis. tanggal jatuh tempo).
CATATAN: Kode ini memerlukan penyesuaian tambahan untuk hari libur tetapi sesuai dengan asumsi di atas, kode ini harus mengecualikan hari libur pada tanggal mulai.
staticreadonlyint[,] _diffOffset =
{
// Su M Tu W Th F Sa
{0, 1, 2, 3, 4, 5, 5}, // Su
{4, 0, 1, 2, 3, 4, 4}, // M
{3, 4, 0, 1, 2, 3, 3}, // Tu
{2, 3, 4, 0, 1, 2, 2}, // W
{1, 2, 3, 4, 0, 1, 1}, // Th
{0, 1, 2, 3, 4, 0, 0}, // F
{0, 1, 2, 3, 4, 5, 0}, // Sa
};
publicstaticintGetWeekdaysDiff(this DateTime dtStart, DateTime dtEnd)
{
int daysDiff = (int)(dtEnd - dtStart).TotalDays;
return daysDiff >= 0
? 5 * (daysDiff / 7) + _diffOffset[(int) dtStart.DayOfWeek, (int) dtEnd.DayOfWeek]
: 5 * (daysDiff / 7) - _diffOffset[6 - (int) dtStart.DayOfWeek, 6 - (int) dtEnd.DayOfWeek];
}
Saya menemukan bahwa sebagian besar solusi lain pada stack overflow lambat (berulang) atau terlalu kompleks dan banyak yang salah. Moral dari cerita ini adalah ... Jangan percaya kecuali Anda telah mengujinya secara mendalam !!
Ide untuk ini berasal dari solusi SQL yang saya temukan di stack overflow. Ide mereka kuat tetapi sayangnya itu juga memiliki bug. Ini berfungsi untuk nilai + ve tetapi pemetaan tabel pemeta mereka salah untuk nilai -ve.
Tony O'Hagan
4
Berikut beberapa kode untuk tujuan itu, dengan hari libur Swedia tetapi Anda dapat menyesuaikan hari libur apa yang dihitung. Perhatikan bahwa saya menambahkan batas yang mungkin ingin Anda hapus, tetapi itu untuk sistem berbasis web dan saya tidak ingin siapa pun memasukkan tanggal besar untuk memonopoli proses
publicstaticintGetWorkdays(DateTime from ,DateTime to)
{
int limit = 9999;
int counter = 0;
DateTime current = from;
int result = 0;
if (from > to)
{
DateTime temp = from;
from = to;
to = temp;
}
if (from >= to)
{
return0;
}
while (current <= to && counter < limit)
{
if (IsSwedishWorkday(current))
{
result++;
}
current = current.AddDays(1);
counter++;
}
return result;
}
publicstaticboolIsSwedishWorkday(DateTime date)
{
return (!IsSwedishHoliday(date) && date.DayOfWeek != DayOfWeek.Saturday && date.DayOfWeek != DayOfWeek.Sunday);
}
publicstaticboolIsSwedishHoliday(DateTime date)
{
return (
IsSameDay(GetEpiphanyDay(date.Year), date) ||
IsSameDay(GetMayDay(date.Year), date) ||
IsSameDay(GetSwedishNationalDay(date.Year), date) ||
IsSameDay(GetChristmasDay(date.Year), date) ||
IsSameDay(GetBoxingDay(date.Year), date) ||
IsSameDay(GetGoodFriday(date.Year), date) ||
IsSameDay(GetAscensionDay(date.Year), date) ||
IsSameDay(GetAllSaintsDay(date.Year), date) ||
IsSameDay(GetMidsummersDay(date.Year), date) ||
IsSameDay(GetPentecostDay(date.Year), date) ||
IsSameDay(GetEasterMonday(date.Year), date) ||
IsSameDay(GetNewYearsDay(date.Year), date) ||
IsSameDay(GetEasterDay(date.Year), date)
);
}
// Trettondagenpublicstatic DateTime GetEpiphanyDay(int year)
{
returnnew DateTime(year, 1, 6);
}
// Första majpublicstatic DateTime GetMayDay(int year)
{
returnnew DateTime(year,5,1);
}
// Juldagenpublicstatic DateTime GetSwedishNationalDay(int year)
{
returnnew DateTime(year, 6, 6);
}
// Juldagenpublicstatic DateTime GetNewYearsDay(int year)
{
returnnew DateTime(year,1,1);
}
// Juldagenpublicstatic DateTime GetChristmasDay(int year)
{
returnnew DateTime(year,12,25);
}
// Annandag julpublicstatic DateTime GetBoxingDay(int year)
{
returnnew DateTime(year, 12, 26);
}
// Långfredagenpublicstatic DateTime GetGoodFriday(int year)
{
return GetEasterDay(year).AddDays(-3);
}
// Kristi himmelsfärdsdagpublicstatic DateTime GetAscensionDay(int year)
{
return GetEasterDay(year).AddDays(5*7+4);
}
// Midsommarpublicstatic DateTime GetAllSaintsDay(int year)
{
DateTime result = new DateTime(year,10,31);
while (result.DayOfWeek != DayOfWeek.Saturday)
{
result = result.AddDays(1);
}
return result;
}
// Midsommarpublicstatic DateTime GetMidsummersDay(int year)
{
DateTime result = new DateTime(year, 6, 20);
while (result.DayOfWeek != DayOfWeek.Saturday)
{
result = result.AddDays(1);
}
return result;
}
// Pingstdagenpublicstatic DateTime GetPentecostDay(int year)
{
return GetEasterDay(year).AddDays(7 * 7);
}
// Annandag påskpublicstatic DateTime GetEasterMonday(int year)
{
return GetEasterDay(year).AddDays(1);
}
publicstatic DateTime GetEasterDay(int y)
{
double c;
double n;
double k;
double i;
double j;
double l;
double m;
double d;
c = System.Math.Floor(y / 100.0);
n = y - 19 * System.Math.Floor(y / 19.0);
k = System.Math.Floor((c - 17) / 25.0);
i = c - System.Math.Floor(c / 4) - System.Math.Floor((c - k) / 3) + 19 * n + 15;
i = i - 30 * System.Math.Floor(i / 30);
i = i - System.Math.Floor(i / 28) * (1 - System.Math.Floor(i / 28) * System.Math.Floor(29 / (i + 1)) * System.Math.Floor((21 - n) / 11));
j = y + System.Math.Floor(y / 4.0) + i + 2 - c + System.Math.Floor(c / 4);
j = j - 7 * System.Math.Floor(j / 7);
l = i - j;
m = 3 + System.Math.Floor((l + 40) / 44);// month
d = l + 28 - 31 * System.Math.Floor(m / 4);// daydouble days = ((m == 3) ? d : d + 31);
DateTime result = new DateTime(y, 3, 1).AddDays(days-1);
return result;
}
fungsi issamedate hilang tetapi hanya isSameDay bool statis publik (DateTime date1, DateTime date2) {return date1.Date == date2.Date; }
Choco Smith
Anda bisa menggunakan tabel pencarian larik int daripada membuat contoh objek Tanggal baru.
TheRealChx101
3
Berikut kode contoh singkatnya. Ini adalah metode kelas, jadi hanya akan berfungsi di dalam kelas Anda. Jika Anda menginginkannya static, ubah tanda tangan menjadi private static(atau public static).
private IEnumerable<DateTime> GetWorkingDays(DateTime sd, DateTime ed)
{
for (var d = sd; d <= ed; d = d.AddDays(1))
if (d.DayOfWeek != DayOfWeek.Saturday && d.DayOfWeek != DayOfWeek.Sunday)
yieldreturn d;
}
Metode ini membuat variabel loop d, menginisialisasinya ke hari mulai sd, lalu menambah satu hari setiap iteration ( d = d.AddDays(1)).
Ini mengembalikan nilai yang diinginkan menggunakan yield, yang membuat file iterator. Hal yang keren tentang iterator adalah bahwa mereka tidak menyimpan semua nilai IEnumerabledalam memori, hanya memanggil masing-masing secara berurutan. Artinya, Anda dapat memanggil metode ini dari awal waktu hingga sekarang tanpa harus khawatir kehabisan memori.
Metode ini tidak mengembalikan jumlah hari kerja antara dua tanggal, metode ini mengembalikan tanggal bisnis antara dua tanggal. Kode yang Anda usulkan sangat bersih dan saya suka penggunaan hasil, tetapi tidak menjawab pertanyaan itu.
Martin
3
Saya banyak mencari algoritma yang mudah dicerna untuk menghitung hari kerja antara 2 tanggal, dan juga untuk mengecualikan hari libur nasional, dan akhirnya saya memutuskan untuk menggunakan pendekatan ini:
publicstaticintNumberOfWorkingDaysBetween2Dates(DateTime start,DateTime due,IEnumerable<DateTime> holidays)
{
var dic = new Dictionary<DateTime, DayOfWeek>();
var totalDays = (due - start).Days;
for (int i = 0; i < totalDays + 1; i++)
{
if (!holidays.Any(x => x == start.AddDays(i)))
dic.Add(start.AddDays(i), start.AddDays(i).DayOfWeek);
}
return dic.Where(x => x.Value != DayOfWeek.Saturday && x.Value != DayOfWeek.Sunday).Count();
}
Pada dasarnya saya ingin mengikuti setiap tanggal dan mengevaluasi kondisi saya:
Bukankah hari Sabtu
Bukankah hari Minggu
Bukan hari libur nasional
tetapi juga saya ingin menghindari pengulangan tanggal.
Dengan menjalankan dan mengukur waktu yang dibutuhkan untuk mengevaluasi 1 tahun penuh, saya mendapatkan hasil sebagai berikut:
staticvoidMain(string[] args)
{
var start = new DateTime(2017, 1, 1);
var due = new DateTime(2017, 12, 31);
var sw = Stopwatch.StartNew();
var days = NumberOfWorkingDaysBetween2Dates(start, due,NationalHolidays());
sw.Stop();
Console.WriteLine($"Total working days = {days} --- time: {sw.Elapsed}");
Console.ReadLine();
// result is:// Total working days = 249-- - time: 00:00:00.0269087
}
Saya pikir tidak ada jawaban di atas yang benar. Tak satu pun dari mereka menyelesaikan semua kasus khusus seperti ketika tanggal dimulai dan berakhir pada tengah akhir pekan, ketika tanggal dimulai pada hari Jumat dan berakhir pada Senin depan, dll. Selain itu, mereka semua membulatkan perhitungan menjadi keseluruhan hari, jadi jika tanggal mulai di tengah hari sabtu misalnya, itu akan mengurangi satu hari penuh dari hari kerja, memberikan hasil yang salah ...
Bagaimanapun, inilah solusi saya yang cukup efisien dan sederhana dan berfungsi untuk semua kasus. Triknya adalah menemukan hari Senin sebelumnya untuk tanggal mulai dan berakhir, dan kemudian lakukan kompensasi kecil ketika mulai dan akhir terjadi selama akhir pekan:
Metode ini tidak menggunakan loop apa pun dan sebenarnya cukup sederhana. Ini memperluas rentang tanggal menjadi minggu penuh karena kita tahu bahwa setiap minggu memiliki 5 hari kerja. Kemudian menggunakan tabel pencarian untuk menemukan jumlah hari kerja yang akan dikurangi dari awal dan akhir untuk mendapatkan hasil yang benar. Saya telah memperluas kalkulasi untuk membantu menunjukkan apa yang sedang terjadi, tetapi semuanya dapat diringkas menjadi satu baris jika diperlukan.
Bagaimanapun, ini berhasil untuk saya dan jadi saya pikir saya akan mempostingnya di sini seandainya itu dapat membantu orang lain. Selamat membuat kode.
Perhitungan
t: Jumlah total hari di antara tanggal (1 jika min = maks)
a + b: Diperlukan hari ekstra untuk mengembangkan total menjadi minggu penuh
k: 1.4 adalah jumlah hari kerja per minggu, yaitu (t / 7) * 5
c: Jumlah hari kerja yang akan dikurangi dari total
m: Tabel pemeta yang digunakan untuk menemukan nilai "c" untuk setiap hari dalam seminggu
Budaya
Kode mengasumsikan hari kerja Senin sampai Jumat. Untuk budaya lain, seperti Minggu hingga Kamis, Anda harus mengimbangi tanggal sebelum penghitungan.
metode
publicintWeekdays(DateTime min, DateTime max)
{
if (min.Date > max.Date) thrownew Exception("Invalid date span");
var t = (max.AddDays(1).Date - min.Date).TotalDays;
var a = (int) min.DayOfWeek;
var b = 6 - (int) max.DayOfWeek;
var k = 1.4;
var m = newint[]{0, 0, 1, 2, 3, 4, 5};
var c = m[a] + m[b];
return (int)((t + a + b) / k) - c;
}
bagaimana Anda bisa mendapatkan K dengan nilai 1.4?
toha
0
Saya hanya akan membagikan solusi saya. Ini berhasil untuk saya, mungkin saya hanya tidak memperhatikan / tahu bahwa ada bug. Saya mulai dengan minggu pertama yang belum selesai jika ada. satu minggu penuh adalah dari hari Minggu hingga Sabtu, jadi jika (int) _now.DayOfWeek bukan 0 (Minggu), minggu pertama tidak lengkap.
Saya hanya mengurangi 1 hitungan minggu pertama untuk minggu pertama Sabtu lalu menambahkannya ke hitungan baru;
Kemudian saya mendapatkan minggu terakhir yang tidak lengkap, lalu kurangi 1 untuk minggu itu lalu tambahkan ke hitungan baru.
Kemudian akhirnya, jumlah minggu lengkap dikalikan dengan 5 (hari kerja) ditambahkan ke hitungan baru.
Saya mengalami kesulitan menemukan versi TSQL yang solid dari kode ini. Di bawah ini pada dasarnya adalah konversi kode C # di sini dengan penambahan tabel Liburan yang harus digunakan untuk menghitung hari libur.
CREATE TABLE dbo.Holiday
(
HolidayDt DATE NOT NULL,
Name NVARCHAR(50) NOT NULL,
IsWeekday BIT NOT NULL,
CONSTRAINT PK_Holiday PRIMARY KEY (HolidayDt)
)
GO
CREATE INDEX IDX_Holiday ON Holiday (HolidayDt, IsWeekday)
GO
CREATE function dbo.GetBusinessDays
(
@FirstDay datetime,
@LastDay datetime
)
RETURNS INT
AS
BEGIN
DECLARE @BusinessDays INT, @FullWeekCount INT
SELECT @FirstDay = CONVERT(DATETIME,CONVERT(DATE,@FirstDay))
, @LastDay = CONVERT(DATETIME,CONVERT(DATE,@LastDay))
IF @FirstDay > @LastDay
RETURN NULL;
SELECT @BusinessDays = DATEDIFF(DAY, @FirstDay, @LastDay) + 1
SELECT @FullWeekCount = @BusinessDays / 7;
-- find outif there are weekends during the time exceedng the full weeks
IF @BusinessDays > (@FullWeekCount * 7)
BEGIN
-- we are here to find outif there is a 1-day or 2-days weekend
-- in the time interval remaining after subtracting the complete weeks
DECLARE @firstDayOfWeek INT, @lastDayOfWeek INT;
SELECT @firstDayOfWeek = DATEPART(DW, @FirstDay), @lastDayOfWeek = DATEPART(DW, @LastDay);
IF @lastDayOfWeek < @firstDayOfWeek
SELECT @lastDayOfWeek = @lastDayOfWeek + 7;
IF @firstDayOfWeek <= 6BEGIN
IF (@lastDayOfWeek >= 7) --Both Saturday and Sunday are in the remaining time interval
BEGIN
SELECT @BusinessDays = @BusinessDays - 2
END
ELSE IF @lastDayOfWeek>=6 --Only Saturday isin the remaining time interval
BEGIN
SELECT @BusinessDays = @BusinessDays - 1
END
END
ELSE IF @firstDayOfWeek <= 7 AND @lastDayOfWeek >=7 -- Only Sunday isin the remaining time interval
BEGIN
SELECT @BusinessDays = @BusinessDays - 1
END
END
-- subtract the weekends during the full weeks in the interval
DECLARE @Holidays INT;
SELECT @Holidays = COUNT(*)
FROM Holiday
WHERE HolidayDt BETWEEN @FirstDay AND @LastDay
AND IsWeekday = CAST(1 AS BIT)
SELECT @BusinessDays = @BusinessDays - (@FullWeekCount + @FullWeekCount) -- - @Holidays
RETURN @BusinessDays
END
Inilah satu solusi yang sangat sederhana untuk masalah ini. Kami memiliki tanggal mulai, tanggal akhir dan "for loop" untuk menambah hari dan menghitung untuk melihat apakah itu hari kerja atau akhir pekan dengan mengonversi ke string DayOfWeek.
classProgram
{
staticvoidMain(string[] args)
{
DateTime day = new DateTime();
Console.Write("Inser your end date (example: 01/30/2015): ");
DateTime endDate = DateTime.Parse(Console.ReadLine());
int numberOfDays = 0;
for (day = DateTime.Now.Date; day.Date < endDate.Date; day = day.Date.AddDays(1))
{
string dayToString = Convert.ToString(day.DayOfWeek);
if (dayToString != "Saturday" && dayToString != "Sunday") numberOfDays++;
}
Console.WriteLine("Number of working days (not including local holidays) between two dates is "+numberOfDays);
}
}
Berdasarkan komentar yang ditandai sebagai jawaban dan tambalan direkomendasikan, serta -> Versi ini ingin mengubah Hari ke Jam Kerja ... Mempertimbangkan jam pada hari yang sama juga.
///<summary>/// Calculates number of business days, taking into account:/// - weekends (Saturdays and Sundays)/// - bank holidays in the middle of the week///</summary>///<param name="firstDay">First day in the time interval</param>///<param name="lastDay">Last day in the time interval</param>///<param name="bankHolidays">List of bank holidays excluding weekends</param>///<returns>Number of business hours during the 'span'</returns>publicstaticintBusinessHoursUntil(DateTime firstDay, DateTime lastDay, params DateTime[] bankHolidays)
{
var original_firstDay = firstDay;
var original_lastDay = lastDay;
firstDay = firstDay.Date;
lastDay = lastDay.Date;
if (firstDay > lastDay)
return-1; //// throw new ArgumentException("Incorrect last day " + lastDay);
TimeSpan span = lastDay - firstDay;
int businessDays = span.Days + 1;
int fullWeekCount = businessDays / 7;
// find out if there are weekends during the time exceedng the full weeksif (businessDays > fullWeekCount * 7)
{
// we are here to find out if there is a 1-day or 2-days weekend// in the time interval remaining after subtracting the complete weeksint firstDayOfWeek = firstDay.DayOfWeek == DayOfWeek.Sunday ? 7 : (int)firstDay.DayOfWeek;
int lastDayOfWeek = lastDay.DayOfWeek == DayOfWeek.Sunday ? 7 : (int)lastDay.DayOfWeek;
if (lastDayOfWeek < firstDayOfWeek)
lastDayOfWeek += 7;
if (firstDayOfWeek <= 6)
{
if (lastDayOfWeek >= 7)// Both Saturday and Sunday are in the remaining time interval
businessDays -= 2;
elseif (lastDayOfWeek >= 6)// Only Saturday is in the remaining time interval
businessDays -= 1;
}
elseif (firstDayOfWeek <= 7 && lastDayOfWeek >= 7)// Only Sunday is in the remaining time interval
businessDays -= 1;
}
// subtract the weekends during the full weeks in the interval
businessDays -= fullWeekCount + fullWeekCount;
if (bankHolidays != null && bankHolidays.Any())
{
// subtract the number of bank holidays during the time intervalforeach (DateTime bankHoliday in bankHolidays)
{
DateTime bh = bankHoliday.Date;
if (firstDay <= bh && bh <= lastDay)
--businessDays;
}
}
int total_business_hours = 0;
if (firstDay.Date == lastDay.Date)
{//If on the same day, go granular with Hours from the Orginial_*Day values
total_business_hours = (int)(original_lastDay - original_firstDay).TotalHours;
}
else
{//Convert Business-Days to TotalHours
total_business_hours = (int)(firstDay.AddDays(businessDays).AddHours(firstDay.Hour) - firstDay).TotalHours;
}
return total_business_hours;
}
Saya baru saja meningkatkan jawaban @Alexander dan @Slauma untuk mendukung minggu kerja sebagai parameter, untuk kasus di mana Sabtu adalah hari kerja, atau bahkan kasus di mana hanya ada beberapa hari dalam seminggu yang dianggap sebagai hari kerja:
///<summary>/// Calculate the number of business days between two dates, considering:/// - Days of the week that are not considered business days./// - Holidays between these two dates.///</summary>///<param name="fDay">First day of the desired 'span'.</param>///<param name="lDay">Last day of the desired 'span'.</param>///<param name="BusinessDaysOfWeek">Days of the week that are considered to be business days, if NULL considers monday, tuesday, wednesday, thursday and friday as business days of the week.</param>///<param name="Holidays">Holidays, if NULL, considers no holiday.</param>///<returns>Number of business days during the 'span'</returns>publicstaticintBusinessDaysUntil(this DateTime fDay, DateTime lDay, DayOfWeek[] BusinessDaysOfWeek = null, DateTime[] Holidays = null)
{
if (BusinessDaysOfWeek == null)
BusinessDaysOfWeek = new DayOfWeek[] { DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday };
if (Holidays == null)
Holidays = new DateTime[] { };
fDay = fDay.Date;
lDay = lDay.Date;
if (fDay > lDay)
thrownew ArgumentException("Incorrect last day " + lDay);
int bDays = (lDay - fDay).Days + 1;
int fullWeekCount = bDays / 7;
int fullWeekCountMult = 7 - WeekDays.Length;
// Find out if there are weekends during the time exceedng the full weeksif (bDays > (fullWeekCount * 7))
{
int fDayOfWeek = (int)fDay.DayOfWeek;
int lDayOfWeek = (int)lDay.DayOfWeek;
if (fDayOfWeek > lDayOfWeek)
lDayOfWeek += 7;
// If they are the same, we already covered it right before the Holiday subtractionif (lDayOfWeek != fDayOfWeek)
{
// Here we need to see if any of the days between are considered business daysfor (int i = fDayOfWeek; i <= lDayOfWeek; i++)
if (!WeekDays.Contains((DayOfWeek)(i > 6 ? i - 7 : i)))
bDays -= 1;
}
}
// Subtract the days that are not in WeekDays[] during the full weeks in the interval
bDays -= (fullWeekCount * fullWeekCountMult);
// Subtract the number of bank holidays during the time interval
bDays = bDays - Holidays.Select(x => x.Date).Count(x => fDay <= x && x <= lDay);
return bDays;
}
Berikut adalah fungsi yang dapat kita gunakan untuk menghitung hari kerja antara dua tanggal. Saya tidak menggunakan daftar hari libur karena dapat berbeda-beda di setiap negara / wilayah.
Jika kita ingin tetap menggunakannya, kita dapat mengambil argumen ketiga sebagai daftar hari libur dan sebelum menambah hitungan kita harus memeriksa bahwa daftar tidak mengandung d
publicstaticintGetBussinessDaysBetweenTwoDates(DateTime StartDate, DateTime EndDate)
{
if (StartDate > EndDate)
return-1;
int bd = 0;
for (DateTime d = StartDate; d < EndDate; d = d.AddDays(1))
{
if (d.DayOfWeek != DayOfWeek.Saturday && d.DayOfWeek != DayOfWeek.Sunday)
bd++;
}
return bd;
}
Ini adalah ide lain - metode ini memungkinkan untuk menentukan minggu kerja dan hari libur.
Idenya di sini adalah kita menemukan inti rentang tanggal dari hari kerja pertama dalam seminggu hingga hari akhir pekan terakhir dalam seminggu. Hal ini memungkinkan kami menghitung seluruh minggu dengan mudah ( tanpa mengulang semua tanggal). Yang perlu kita lakukan selanjutnya adalah menambahkan hari kerja yang jatuh sebelum awal dan akhir rentang inti ini.
publicstaticintCalculateWorkingDays(
DateTime startDate,
DateTime endDate,
IList<DateTime> holidays,
DayOfWeek firstDayOfWeek,
DayOfWeek lastDayOfWeek)
{
// Make sure the defined working days run contiguouslyif (lastDayOfWeek < firstDayOfWeek)
{
thrownew Exception("Last day of week cannot fall before first day of week!");
}
// Create a list of the days of the week that make-up the weekend by working back// from the firstDayOfWeek and forward from lastDayOfWeek to get the start and end// the weekendvar weekendStart = lastDayOfWeek == DayOfWeek.Saturday ? DayOfWeek.Sunday : lastDayOfWeek + 1;
var weekendEnd = firstDayOfWeek == DayOfWeek.Sunday ? DayOfWeek.Saturday : firstDayOfWeek - 1;
var weekendDays = new List<DayOfWeek>();
var w = weekendStart;
do {
weekendDays.Add(w);
if (w == weekendEnd) break;
w = (w == DayOfWeek.Saturday) ? DayOfWeek.Sunday : w + 1;
} while (true);
// Force simple dates - no time
startDate = startDate.Date;
endDate = endDate.Date;
// Ensure a progessive date rangeif (endDate < startDate)
{
var t = startDate;
startDate = endDate;
endDate = t;
}
// setup some working variables and constantsconstint daysInWeek = 7; // yeah - really!var actualStartDate = startDate; // this will end up on startOfWeek boundaryvar actualEndDate = endDate; // this will end up on weekendEnd boundaryint workingDaysInWeek = daysInWeek - weekendDays.Count;
int workingDays = 0; // the result we are trying to findint leadingDays = 0; // the number of working days leading up to the firstDayOfWeek boundaryint trailingDays = 0; // the number of working days counting back to the weekendEnd boundary// Calculate leading working days// if we aren't on the firstDayOfWeek we need to step forward to the nearestif (startDate.DayOfWeek != firstDayOfWeek)
{
var d = startDate;
do {
if (d.DayOfWeek == firstDayOfWeek || d >= endDate)
{
actualStartDate = d;
break;
}
if (!weekendDays.Contains(d.DayOfWeek))
{
leadingDays++;
}
d = d.AddDays(1);
} while(true);
}
// Calculate trailing working days// if we aren't on the weekendEnd we step back to the nearestif (endDate >= actualStartDate && endDate.DayOfWeek != weekendEnd)
{
var d = endDate;
do {
if (d.DayOfWeek == weekendEnd || d < actualStartDate)
{
actualEndDate = d;
break;
}
if (!weekendDays.Contains(d.DayOfWeek))
{
trailingDays++;
}
d = d.AddDays(-1);
} while(true);
}
// Calculate the inclusive number of days between the actualStartDate and the actualEndDatevar coreDays = (actualEndDate - actualStartDate).Days + 1;
var noWeeks = coreDays / daysInWeek;
// add together leading, core and trailing days
workingDays += noWeeks * workingDaysInWeek;
workingDays += leadingDays;
workingDays += trailingDays;
// Finally remove any holidays that fall within the range.if (holidays != null)
{
workingDays -= holidays.Count(h => h >= startDate && (h <= endDate));
}
return workingDays;
}
Karena saya tidak bisa berkomentar. Ada satu masalah lagi dengan solusi yang diterima di mana hari libur bank dikurangi bahkan ketika mereka berada di akhir pekan. Melihat bagaimana masukan lain dicentang, ini juga cocok.
Oleh karena itu, foreach harus:
// subtract the number of bank holidays during the time intervalforeach (DateTime bankHoliday in bankHolidays)
{
DateTime bh = bankHoliday.Date;
// Do not subtract bank holidays when they fall in the weekend to avoid double subtractionif (bh.DayOfWeek == DayOfWeek.Saturday || bh.DayOfWeek == DayOfWeek.Sunday)
continue;
if (firstDay <= bh && bh <= lastDay)
--businessDays;
}
Berikut adalah pendekatan jika Anda menggunakan MVC. Saya juga telah menghitung hari libur nasional atau hari raya apa pun yang akan dikecualikan dengan mengambilnya dari kalender hari libur yang akan Anda perlukan untuk membuatnya.
Berikut adalah fungsi pembantu yang saya tulis untuk tugas itu. itu juga mengembalikan hitungan akhir pekan melalui outparameter. jika Anda ingin, Anda dapat menyesuaikan hari "akhir pekan" dalam waktu proses untuk negara-negara yang menggunakan hari akhir pekan yang berbeda atau untuk menyertakan hari libur melalui weekendDays[]parameter opsional:
publicstaticintGetNetworkDays(DateTime startDate, DateTime endDate,outint totalWeekenDays, DayOfWeek[] weekendDays = null)
{
if (startDate >= endDate)
{
thrownew Exception("start date can not be greater then or equel to end date");
}
DayOfWeek[] weekends = new DayOfWeek[] { DayOfWeek.Sunday, DayOfWeek.Saturday };
if (weekendDays != null)
{
weekends = weekendDays;
}
var totaldays = (endDate - startDate).TotalDays + 1; // add one to include the first day tovar counter = 0;
var workdaysCounter = 0;
var weekendsCounter = 0;
for (int i = 0; i < totaldays; i++)
{
if (weekends.Contains(startDate.AddDays(counter).DayOfWeek))
{
weekendsCounter++;
}
else
{
workdaysCounter++;
}
counter++;
}
totalWeekenDays = weekendsCounter;
return workdaysCounter;
}
publicstaticintCalculateBusinessDaysInRange(this DateTime startDate, DateTime endDate, params DateTime[] holidayDates)
{
endDate = endDate.Date;
if(startDate > endDate)
thrownew ArgumentException("The end date can not be before the start date!", nameof(endDate));
int accumulator = 0;
DateTime itterator = startDate.Date;
do
{
if(itterator.DayOfWeek != DayOfWeek.Saturday && itterator.DayOfWeek != DayOfWeek.Sunday && !holidayDates.Any(hol => hol.Date == itterator))
{ accumulator++; }
}
while((itterator = itterator.AddDays(1)).Date <= endDate);
return accumulator
}
Saya hanya memposting ini karena terlepas dari semua jawaban luar biasa yang telah diberikan, tidak ada matematika yang masuk akal bagi saya. Ini jelas merupakan metode KISS yang seharusnya berhasil dan dapat dipelihara dengan baik. Memang, jika Anda menghitung rentang yang lebih besar dari 2-3 bulan, ini bukan cara yang paling efektif. Kami hanya menentukan apakah itu hari Sabtu atau Minggu atau tanggal tersebut adalah tanggal hari libur tertentu. Kalau belum kita tambahkan hari kerja. Jika sudah maka semuanya baik-baik saja.
Saya yakin ini bisa lebih disederhanakan dengan LINQ, tapi cara ini jauh lebih mudah untuk dipahami.
Jawaban:
Saya pernah memiliki tugas seperti itu sebelumnya dan saya punya solusinya. Saya akan menghindari menghitung semua hari di antara saat itu dapat dihindari, yang terjadi di sini. Saya bahkan tidak menyebutkan membuat banyak contoh DateTime, seperti yang saya lihat di salah satu jawaban di atas. Ini benar-benar pemborosan daya pemrosesan. Apalagi dalam situasi dunia nyata, ketika Anda harus memeriksa interval waktu beberapa bulan. Lihat kode saya, dengan komentar, di bawah.
/// <summary> /// Calculates number of business days, taking into account: /// - weekends (Saturdays and Sundays) /// - bank holidays in the middle of the week /// </summary> /// <param name="firstDay">First day in the time interval</param> /// <param name="lastDay">Last day in the time interval</param> /// <param name="bankHolidays">List of bank holidays excluding weekends</param> /// <returns>Number of business days during the 'span'</returns> public static int BusinessDaysUntil(this DateTime firstDay, DateTime lastDay, params DateTime[] bankHolidays) { firstDay = firstDay.Date; lastDay = lastDay.Date; if (firstDay > lastDay) throw new ArgumentException("Incorrect last day " + lastDay); TimeSpan span = lastDay - firstDay; int businessDays = span.Days + 1; int fullWeekCount = businessDays / 7; // find out if there are weekends during the time exceedng the full weeks if (businessDays > fullWeekCount*7) { // we are here to find out if there is a 1-day or 2-days weekend // in the time interval remaining after subtracting the complete weeks int firstDayOfWeek = (int) firstDay.DayOfWeek; int lastDayOfWeek = (int) lastDay.DayOfWeek; if (lastDayOfWeek < firstDayOfWeek) lastDayOfWeek += 7; if (firstDayOfWeek <= 6) { if (lastDayOfWeek >= 7)// Both Saturday and Sunday are in the remaining time interval businessDays -= 2; else if (lastDayOfWeek >= 6)// Only Saturday is in the remaining time interval businessDays -= 1; } else if (firstDayOfWeek <= 7 && lastDayOfWeek >= 7)// Only Sunday is in the remaining time interval businessDays -= 1; } // subtract the weekends during the full weeks in the interval businessDays -= fullWeekCount + fullWeekCount; // subtract the number of bank holidays during the time interval foreach (DateTime bankHoliday in bankHolidays) { DateTime bh = bankHoliday.Date; if (firstDay <= bh && bh <= lastDay) --businessDays; } return businessDays; }
Diedit oleh Slauma, Agustus 2011
Jawaban yang bagus! Namun ada sedikit bug. Saya mengambil kebebasan untuk mengedit jawaban ini karena penjawab tidak ada sejak 2009.
Kode di atas mengasumsikan bahwa
DayOfWeek.Sunday
memiliki nilai7
yang tidak demikian. Nilainya sebenarnya0
. Ini mengarah pada perhitungan yang salah jika misalnyafirstDay
danlastDay
keduanya adalah hari Minggu yang sama. Metode ini kembali1
dalam kasus ini tetapi seharusnya begitu0
.Perbaikan termudah untuk bug ini: Ganti kode di atas baris di mana
firstDayOfWeek
danlastDayOfWeek
dideklarasikan sebagai berikut:int firstDayOfWeek = firstDay.DayOfWeek == DayOfWeek.Sunday ? 7 : (int)firstDay.DayOfWeek; int lastDayOfWeek = lastDay.DayOfWeek == DayOfWeek.Sunday ? 7 : (int)lastDay.DayOfWeek;
Sekarang hasilnya adalah:
sumber
&& !(bh.DayOfWeek == DayOfWeek.Sunday || bh.DayOfWeek == DayOfWeek.Saturday)
, itu akan mengurangi hari yang sama dua kali, jika hari libur jatuh di akhir pekan.Baik. Saya pikir inilah saatnya untuk memposting jawaban yang benar:
public static double GetBusinessDays(DateTime startD, DateTime endD) { double calcBusinessDays = 1 + ((endD - startD).TotalDays * 5 - (startD.DayOfWeek - endD.DayOfWeek) * 2) / 7; if (endD.DayOfWeek == DayOfWeek.Saturday) calcBusinessDays--; if (startD.DayOfWeek == DayOfWeek.Sunday) calcBusinessDays--; return calcBusinessDays; }
Sumber Asli:
http://alecpojidaev.wordpress.com/2009/10/29/work-days-calculation-with-c/
Solusi PS yang diposting di atas membuat saya sic karena beberapa alasan.
sumber
Saya tahu pertanyaan ini sudah terpecahkan, tetapi saya pikir saya bisa memberikan jawaban yang lebih lugas yang dapat membantu pengunjung lain di masa depan.
Inilah pendapat saya:
public int GetWorkingDays(DateTime from, DateTime to) { var dayDifference = (int)to.Subtract(from).TotalDays; return Enumerable .Range(1, dayDifference) .Select(x => from.AddDays(x)) .Count(x => x.DayOfWeek != DayOfWeek.Saturday && x.DayOfWeek != DayOfWeek.Sunday); }
Ini adalah kiriman asli saya:
public int GetWorkingDays(DateTime from, DateTime to) { var totalDays = 0; for (var date = from; date < to; date = date.AddDays(1)) { if (date.DayOfWeek != DayOfWeek.Saturday && date.DayOfWeek != DayOfWeek.Sunday) totalDays++; } return totalDays; }
sumber
to > from
. Mungkin itu masalahnya?Tentukan Metode Ekstensi pada DateTime seperti ini:
public static class DateTimeExtensions { public static bool IsWorkingDay(this DateTime date) { return date.DayOfWeek != DayOfWeek.Saturday && date.DayOfWeek != DayOfWeek.Sunday; } }
Kemudian, gunakan dalam klausa Di mana untuk memfilter daftar tanggal yang lebih luas:
var allDates = GetDates(); // method which returns a list of dates // filter dates by working day's var countOfWorkDays = allDates .Where(day => day.IsWorkingDay()) .Count() ;
sumber
Saya menggunakan kode berikut untuk juga memperhitungkan hari libur bank:
public class WorkingDays { public List<DateTime> GetHolidays() { var client = new WebClient(); var json = client.DownloadString("https://www.gov.uk/bank-holidays.json"); var js = new JavaScriptSerializer(); var holidays = js.Deserialize <Dictionary<string, Holidays>>(json); return holidays["england-and-wales"].events.Select(d => d.date).ToList(); } public int GetWorkingDays(DateTime from, DateTime to) { var totalDays = 0; var holidays = GetHolidays(); for (var date = from.AddDays(1); date <= to; date = date.AddDays(1)) { if (date.DayOfWeek != DayOfWeek.Saturday && date.DayOfWeek != DayOfWeek.Sunday && !holidays.Contains(date)) totalDays++; } return totalDays; } } public class Holidays { public string division { get; set; } public List<Event> events { get; set; } } public class Event { public DateTime date { get; set; } public string notes { get; set; } public string title { get; set; } }
Dan Tes Unit:
[TestClass] public class WorkingDays { [TestMethod] public void SameDayIsZero() { var service = new WorkingDays(); var from = new DateTime(2013, 8, 12); Assert.AreEqual(0, service.GetWorkingDays(from, from)); } [TestMethod] public void CalculateDaysInWorkingWeek() { var service = new WorkingDays(); var from = new DateTime(2013, 8, 12); var to = new DateTime(2013, 8, 16); Assert.AreEqual(4, service.GetWorkingDays(from, to), "Mon - Fri = 4"); Assert.AreEqual(1, service.GetWorkingDays(from, new DateTime(2013, 8, 13)), "Mon - Tues = 1"); } [TestMethod] public void NotIncludeWeekends() { var service = new WorkingDays(); var from = new DateTime(2013, 8, 9); var to = new DateTime(2013, 8, 16); Assert.AreEqual(5, service.GetWorkingDays(from, to), "Fri - Fri = 5"); Assert.AreEqual(2, service.GetWorkingDays(from, new DateTime(2013, 8, 13)), "Fri - Tues = 2"); Assert.AreEqual(1, service.GetWorkingDays(from, new DateTime(2013, 8, 12)), "Fri - Mon = 1"); } [TestMethod] public void AccountForHolidays() { var service = new WorkingDays(); var from = new DateTime(2013, 8, 23); Assert.AreEqual(0, service.GetWorkingDays(from, new DateTime(2013, 8, 26)), "Fri - Mon = 0"); Assert.AreEqual(1, service.GetWorkingDays(from, new DateTime(2013, 8, 27)), "Fri - Tues = 1"); } }
sumber
Nah ini telah dipukuli sampai mati. :) Namun saya tetap akan memberikan jawaban lain karena saya membutuhkan sesuatu yang sedikit berbeda. Solusi ini berbeda karena mengembalikan Business TimeSpan antara awal dan akhir, dan Anda dapat mengatur jam kerja hari itu, serta menambahkan hari libur. Jadi, Anda dapat menggunakannya untuk menghitung apakah itu terjadi dalam satu hari, di sepanjang hari, selama akhir pekan, dan bahkan hari libur. Dan Anda bisa mendapatkan hari kerja atau tidak dengan hanya mendapatkan apa yang Anda butuhkan dari objek TimeSpan yang dikembalikan. Dan cara menggunakan daftar hari, Anda dapat melihat betapa mudahnya menambahkan daftar hari non-kerja jika bukan Sab dan Ming yang biasa. Dan saya mengujinya selama setahun, dan tampaknya super cepat.
Saya hanya berharap kode yang ditempelkan akurat. Tapi saya tahu itu berhasil.
public static TimeSpan GetBusinessTimespanBetween( DateTime start, DateTime end, TimeSpan workdayStartTime, TimeSpan workdayEndTime, List<DateTime> holidays = null) { if (end < start) throw new ArgumentException("start datetime must be before end datetime."); // Just create an empty list for easier coding. if (holidays == null) holidays = new List<DateTime>(); if (holidays.Where(x => x.TimeOfDay.Ticks > 0).Any()) throw new ArgumentException("holidays can not have a TimeOfDay, only the Date."); var nonWorkDays = new List<DayOfWeek>() { DayOfWeek.Saturday, DayOfWeek.Sunday }; var startTime = start.TimeOfDay; // If the start time is before the starting hours, set it to the starting hour. if (startTime < workdayStartTime) startTime = workdayStartTime; var timeBeforeEndOfWorkDay = workdayEndTime - startTime; // If it's after the end of the day, then this time lapse doesn't count. if (timeBeforeEndOfWorkDay.TotalSeconds < 0) timeBeforeEndOfWorkDay = new TimeSpan(); // If start is during a non work day, it doesn't count. if (nonWorkDays.Contains(start.DayOfWeek)) timeBeforeEndOfWorkDay = new TimeSpan(); else if (holidays.Contains(start.Date)) timeBeforeEndOfWorkDay = new TimeSpan(); var endTime = end.TimeOfDay; // If the end time is after the ending hours, set it to the ending hour. if (endTime > workdayEndTime) endTime = workdayEndTime; var timeAfterStartOfWorkDay = endTime - workdayStartTime; // If it's before the start of the day, then this time lapse doesn't count. if (timeAfterStartOfWorkDay.TotalSeconds < 0) timeAfterStartOfWorkDay = new TimeSpan(); // If end is during a non work day, it doesn't count. if (nonWorkDays.Contains(end.DayOfWeek)) timeAfterStartOfWorkDay = new TimeSpan(); else if (holidays.Contains(end.Date)) timeAfterStartOfWorkDay = new TimeSpan(); // Easy scenario if the times are during the day day. if (start.Date.CompareTo(end.Date) == 0) { if (nonWorkDays.Contains(start.DayOfWeek)) return new TimeSpan(); else if (holidays.Contains(start.Date)) return new TimeSpan(); return endTime - startTime; } else { var timeBetween = end - start; var daysBetween = (int)Math.Floor(timeBetween.TotalDays); var dailyWorkSeconds = (int)Math.Floor((workdayEndTime - workdayStartTime).TotalSeconds); var businessDaysBetween = 0; // Now the fun begins with calculating the actual Business days. if (daysBetween > 0) { var nextStartDay = start.AddDays(1).Date; var dayBeforeEnd = end.AddDays(-1).Date; for (DateTime d = nextStartDay; d <= dayBeforeEnd; d = d.AddDays(1)) { if (nonWorkDays.Contains(d.DayOfWeek)) continue; else if (holidays.Contains(d.Date)) continue; businessDaysBetween++; } } var dailyWorkSecondsToAdd = dailyWorkSeconds * businessDaysBetween; var output = timeBeforeEndOfWorkDay + timeAfterStartOfWorkDay; output = output + new TimeSpan(0, 0, dailyWorkSecondsToAdd); return output; } }
Dan berikut ini kode pengujiannya: Perhatikan bahwa Anda hanya perlu meletakkan fungsi ini di kelas yang disebut DateHelper agar kode pengujian berfungsi.
[TestMethod] public void TestGetBusinessTimespanBetween() { var workdayStart = new TimeSpan(8, 0, 0); var workdayEnd = new TimeSpan(17, 0, 0); var holidays = new List<DateTime>() { new DateTime(2018, 1, 15), // a Monday new DateTime(2018, 2, 15) // a Thursday }; var testdata = new[] { new { expectedMinutes = 0, start = new DateTime(2016, 10, 19, 9, 50, 0), end = new DateTime(2016, 10, 19, 9, 50, 0) }, new { expectedMinutes = 10, start = new DateTime(2016, 10, 19, 9, 50, 0), end = new DateTime(2016, 10, 19, 10, 0, 0) }, new { expectedMinutes = 5, start = new DateTime(2016, 10, 19, 7, 50, 0), end = new DateTime(2016, 10, 19, 8, 5, 0) }, new { expectedMinutes = 5, start = new DateTime(2016, 10, 19, 16, 55, 0), end = new DateTime(2016, 10, 19, 17, 5, 0) }, new { expectedMinutes = 15, start = new DateTime(2016, 10, 19, 16, 50, 0), end = new DateTime(2016, 10, 20, 8, 5, 0) }, new { expectedMinutes = 10, start = new DateTime(2016, 10, 19, 16, 50, 0), end = new DateTime(2016, 10, 20, 7, 55, 0) }, new { expectedMinutes = 5, start = new DateTime(2016, 10, 19, 17, 10, 0), end = new DateTime(2016, 10, 20, 8, 5, 0) }, new { expectedMinutes = 0, start = new DateTime(2016, 10, 19, 17, 10, 0), end = new DateTime(2016, 10, 20, 7, 5, 0) }, new { expectedMinutes = 545, start = new DateTime(2016, 10, 19, 12, 10, 0), end = new DateTime(2016, 10, 20, 12, 15, 0) }, // Spanning multiple weekdays new { expectedMinutes = 835, start = new DateTime(2016, 10, 19, 12, 10, 0), end = new DateTime(2016, 10, 21, 8, 5, 0) }, // Spanning multiple weekdays new { expectedMinutes = 1375, start = new DateTime(2016, 10, 18, 12, 10, 0), end = new DateTime(2016, 10, 21, 8, 5, 0) }, // Spanning from a Thursday to a Tuesday, 5 mins short of complete day. new { expectedMinutes = 1615, start = new DateTime(2016, 10, 20, 12, 10, 0), end = new DateTime(2016, 10, 25, 12, 5, 0) }, // Spanning from a Thursday to a Tuesday, 5 mins beyond complete day. new { expectedMinutes = 1625, start = new DateTime(2016, 10, 20, 12, 10, 0), end = new DateTime(2016, 10, 25, 12, 15, 0) }, // Spanning from a Friday to a Monday, 5 mins beyond complete day. new { expectedMinutes = 545, start = new DateTime(2016, 10, 21, 12, 10, 0), end = new DateTime(2016, 10, 24, 12, 15, 0) }, // Spanning from a Friday to a Monday, 5 mins short complete day. new { expectedMinutes = 535, start = new DateTime(2016, 10, 21, 12, 10, 0), end = new DateTime(2016, 10, 24, 12, 5, 0) }, // Spanning from a Saturday to a Monday, 5 mins short complete day. new { expectedMinutes = 245, start = new DateTime(2016, 10, 22, 12, 10, 0), end = new DateTime(2016, 10, 24, 12, 5, 0) }, // Spanning from a Saturday to a Sunday, 5 mins beyond complete day. new { expectedMinutes = 0, start = new DateTime(2016, 10, 22, 12, 10, 0), end = new DateTime(2016, 10, 23, 12, 15, 0) }, // Times within the same Saturday. new { expectedMinutes = 0, start = new DateTime(2016, 10, 22, 12, 10, 0), end = new DateTime(2016, 10, 23, 12, 15, 0) }, // Spanning from a Saturday to the Sunday next week. new { expectedMinutes = 2700, start = new DateTime(2016, 10, 22, 12, 10, 0), end = new DateTime(2016, 10, 30, 12, 15, 0) }, // Spanning a year. new { expectedMinutes = 143355, start = new DateTime(2016, 10, 22, 12, 10, 0), end = new DateTime(2017, 10, 30, 12, 15, 0) }, // Spanning a year with 2 holidays. new { expectedMinutes = 142815, start = new DateTime(2017, 10, 22, 12, 10, 0), end = new DateTime(2018, 10, 30, 12, 15, 0) }, }; foreach (var item in testdata) { Assert.AreEqual(item.expectedMinutes, DateHelper.GetBusinessTimespanBetween( item.start, item.end, workdayStart, workdayEnd, holidays) .TotalMinutes); } }
sumber
Solusi ini menghindari iterasi, bekerja untuk perbedaan hari kerja + ve dan -ve dan menyertakan rangkaian pengujian unit untuk meregresi terhadap metode penghitungan hari kerja yang lebih lambat. Saya juga menyertakan metode ringkas untuk menambahkan hari kerja yang juga berfungsi dengan cara non-iteratif yang sama.
Pengujian unit mencakup beberapa ribu kombinasi tanggal untuk menguji secara menyeluruh semua kombinasi awal / akhir hari kerja dengan rentang tanggal kecil dan besar.
Penting : Kami membuat asumsi bahwa kami menghitung hari dengan mengecualikan tanggal mulai dan termasuk tanggal akhir. Ini penting saat menghitung hari kerja karena hari mulai / akhir spesifik yang Anda sertakan / kecualikan memengaruhi hasil. Ini juga memastikan bahwa perbedaan antara dua hari yang sama selalu nol dan bahwa kami hanya menyertakan hari kerja penuh karena biasanya Anda ingin jawabannya benar kapan saja pada tanggal mulai saat ini (seringkali hari ini) dan menyertakan tanggal akhir lengkap (mis. tanggal jatuh tempo).
CATATAN: Kode ini memerlukan penyesuaian tambahan untuk hari libur tetapi sesuai dengan asumsi di atas, kode ini harus mengecualikan hari libur pada tanggal mulai.
Tambahkan hari kerja:
private static readonly int[,] _addOffset = { // 0 1 2 3 4 {0, 1, 2, 3, 4}, // Su 0 {0, 1, 2, 3, 4}, // M 1 {0, 1, 2, 3, 6}, // Tu 2 {0, 1, 4, 5, 6}, // W 3 {0, 1, 4, 5, 6}, // Th 4 {0, 3, 4, 5, 6}, // F 5 {0, 2, 3, 4, 5}, // Sa 6 }; public static DateTime AddWeekdays(this DateTime date, int weekdays) { int extraDays = weekdays % 5; int addDays = weekdays >= 0 ? (weekdays / 5) * 7 + _addOffset[(int)date.DayOfWeek, extraDays] : (weekdays / 5) * 7 - _addOffset[6 - (int)date.DayOfWeek, -extraDays]; return date.AddDays(addDays); }
Hitung perbedaan hari kerja:
static readonly int[,] _diffOffset = { // Su M Tu W Th F Sa {0, 1, 2, 3, 4, 5, 5}, // Su {4, 0, 1, 2, 3, 4, 4}, // M {3, 4, 0, 1, 2, 3, 3}, // Tu {2, 3, 4, 0, 1, 2, 2}, // W {1, 2, 3, 4, 0, 1, 1}, // Th {0, 1, 2, 3, 4, 0, 0}, // F {0, 1, 2, 3, 4, 5, 0}, // Sa }; public static int GetWeekdaysDiff(this DateTime dtStart, DateTime dtEnd) { int daysDiff = (int)(dtEnd - dtStart).TotalDays; return daysDiff >= 0 ? 5 * (daysDiff / 7) + _diffOffset[(int) dtStart.DayOfWeek, (int) dtEnd.DayOfWeek] : 5 * (daysDiff / 7) - _diffOffset[6 - (int) dtStart.DayOfWeek, 6 - (int) dtEnd.DayOfWeek]; }
Saya menemukan bahwa sebagian besar solusi lain pada stack overflow lambat (berulang) atau terlalu kompleks dan banyak yang salah. Moral dari cerita ini adalah ... Jangan percaya kecuali Anda telah mengujinya secara mendalam !!
Pengujian unit berdasarkan pengujian Kombinatorial NUnit dan ekstensi ShouldBe NUnit.
[TestFixture] public class DateTimeExtensionsTests { /// <summary> /// Exclude start date, Include end date /// </summary> /// <param name="dtStart"></param> /// <param name="dtEnd"></param> /// <returns></returns> private IEnumerable<DateTime> GetDateRange(DateTime dtStart, DateTime dtEnd) { Console.WriteLine(@"dtStart={0:yy-MMM-dd ddd}, dtEnd={1:yy-MMM-dd ddd}", dtStart, dtEnd); TimeSpan diff = dtEnd - dtStart; Console.WriteLine(diff); if (dtStart <= dtEnd) { for (DateTime dt = dtStart.AddDays(1); dt <= dtEnd; dt = dt.AddDays(1)) { Console.WriteLine(@"dt={0:yy-MMM-dd ddd}", dt); yield return dt; } } else { for (DateTime dt = dtStart.AddDays(-1); dt >= dtEnd; dt = dt.AddDays(-1)) { Console.WriteLine(@"dt={0:yy-MMM-dd ddd}", dt); yield return dt; } } } [Test, Combinatorial] public void TestGetWeekdaysDiff( [Values(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 20, 30)] int startDay, [Values(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 20, 30)] int endDay, [Values(7)] int startMonth, [Values(7)] int endMonth) { // Arrange DateTime dtStart = new DateTime(2016, startMonth, startDay); DateTime dtEnd = new DateTime(2016, endMonth, endDay); int nDays = GetDateRange(dtStart, dtEnd) .Count(dt => dt.DayOfWeek != DayOfWeek.Saturday && dt.DayOfWeek != DayOfWeek.Sunday); if (dtEnd < dtStart) nDays = -nDays; Console.WriteLine(@"countBusDays={0}", nDays); // Act / Assert dtStart.GetWeekdaysDiff(dtEnd).ShouldBe(nDays); } [Test, Combinatorial] public void TestAddWeekdays( [Values(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 20, 30)] int startDay, [Values(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 20, 30)] int weekdays) { DateTime dtStart = new DateTime(2016, 7, startDay); DateTime dtEnd1 = dtStart.AddWeekdays(weekdays); // ADD dtStart.GetWeekdaysDiff(dtEnd1).ShouldBe(weekdays); DateTime dtEnd2 = dtStart.AddWeekdays(-weekdays); // SUBTRACT dtStart.GetWeekdaysDiff(dtEnd2).ShouldBe(-weekdays); } }
sumber
Berikut beberapa kode untuk tujuan itu, dengan hari libur Swedia tetapi Anda dapat menyesuaikan hari libur apa yang dihitung. Perhatikan bahwa saya menambahkan batas yang mungkin ingin Anda hapus, tetapi itu untuk sistem berbasis web dan saya tidak ingin siapa pun memasukkan tanggal besar untuk memonopoli proses
public static int GetWorkdays(DateTime from ,DateTime to) { int limit = 9999; int counter = 0; DateTime current = from; int result = 0; if (from > to) { DateTime temp = from; from = to; to = temp; } if (from >= to) { return 0; } while (current <= to && counter < limit) { if (IsSwedishWorkday(current)) { result++; } current = current.AddDays(1); counter++; } return result; } public static bool IsSwedishWorkday(DateTime date) { return (!IsSwedishHoliday(date) && date.DayOfWeek != DayOfWeek.Saturday && date.DayOfWeek != DayOfWeek.Sunday); } public static bool IsSwedishHoliday(DateTime date) { return ( IsSameDay(GetEpiphanyDay(date.Year), date) || IsSameDay(GetMayDay(date.Year), date) || IsSameDay(GetSwedishNationalDay(date.Year), date) || IsSameDay(GetChristmasDay(date.Year), date) || IsSameDay(GetBoxingDay(date.Year), date) || IsSameDay(GetGoodFriday(date.Year), date) || IsSameDay(GetAscensionDay(date.Year), date) || IsSameDay(GetAllSaintsDay(date.Year), date) || IsSameDay(GetMidsummersDay(date.Year), date) || IsSameDay(GetPentecostDay(date.Year), date) || IsSameDay(GetEasterMonday(date.Year), date) || IsSameDay(GetNewYearsDay(date.Year), date) || IsSameDay(GetEasterDay(date.Year), date) ); } // Trettondagen public static DateTime GetEpiphanyDay(int year) { return new DateTime(year, 1, 6); } // Första maj public static DateTime GetMayDay(int year) { return new DateTime(year,5,1); } // Juldagen public static DateTime GetSwedishNationalDay(int year) { return new DateTime(year, 6, 6); } // Juldagen public static DateTime GetNewYearsDay(int year) { return new DateTime(year,1,1); } // Juldagen public static DateTime GetChristmasDay(int year) { return new DateTime(year,12,25); } // Annandag jul public static DateTime GetBoxingDay(int year) { return new DateTime(year, 12, 26); } // Långfredagen public static DateTime GetGoodFriday(int year) { return GetEasterDay(year).AddDays(-3); } // Kristi himmelsfärdsdag public static DateTime GetAscensionDay(int year) { return GetEasterDay(year).AddDays(5*7+4); } // Midsommar public static DateTime GetAllSaintsDay(int year) { DateTime result = new DateTime(year,10,31); while (result.DayOfWeek != DayOfWeek.Saturday) { result = result.AddDays(1); } return result; } // Midsommar public static DateTime GetMidsummersDay(int year) { DateTime result = new DateTime(year, 6, 20); while (result.DayOfWeek != DayOfWeek.Saturday) { result = result.AddDays(1); } return result; } // Pingstdagen public static DateTime GetPentecostDay(int year) { return GetEasterDay(year).AddDays(7 * 7); } // Annandag påsk public static DateTime GetEasterMonday(int year) { return GetEasterDay(year).AddDays(1); } public static DateTime GetEasterDay(int y) { double c; double n; double k; double i; double j; double l; double m; double d; c = System.Math.Floor(y / 100.0); n = y - 19 * System.Math.Floor(y / 19.0); k = System.Math.Floor((c - 17) / 25.0); i = c - System.Math.Floor(c / 4) - System.Math.Floor((c - k) / 3) + 19 * n + 15; i = i - 30 * System.Math.Floor(i / 30); i = i - System.Math.Floor(i / 28) * (1 - System.Math.Floor(i / 28) * System.Math.Floor(29 / (i + 1)) * System.Math.Floor((21 - n) / 11)); j = y + System.Math.Floor(y / 4.0) + i + 2 - c + System.Math.Floor(c / 4); j = j - 7 * System.Math.Floor(j / 7); l = i - j; m = 3 + System.Math.Floor((l + 40) / 44);// month d = l + 28 - 31 * System.Math.Floor(m / 4);// day double days = ((m == 3) ? d : d + 31); DateTime result = new DateTime(y, 3, 1).AddDays(days-1); return result; }
sumber
Berikut kode contoh singkatnya. Ini adalah metode kelas, jadi hanya akan berfungsi di dalam kelas Anda. Jika Anda menginginkannya
static
, ubah tanda tangan menjadiprivate static
(ataupublic static
).private IEnumerable<DateTime> GetWorkingDays(DateTime sd, DateTime ed) { for (var d = sd; d <= ed; d = d.AddDays(1)) if (d.DayOfWeek != DayOfWeek.Saturday && d.DayOfWeek != DayOfWeek.Sunday) yield return d; }
Metode ini membuat variabel loop
d
, menginisialisasinya ke hari mulaisd
, lalu menambah satu hari setiap iteration (d = d.AddDays(1)
).Ini mengembalikan nilai yang diinginkan menggunakan
yield
, yang membuat fileiterator
. Hal yang keren tentang iterator adalah bahwa mereka tidak menyimpan semua nilaiIEnumerable
dalam memori, hanya memanggil masing-masing secara berurutan. Artinya, Anda dapat memanggil metode ini dari awal waktu hingga sekarang tanpa harus khawatir kehabisan memori.sumber
Saya banyak mencari algoritma yang mudah dicerna untuk menghitung hari kerja antara 2 tanggal, dan juga untuk mengecualikan hari libur nasional, dan akhirnya saya memutuskan untuk menggunakan pendekatan ini:
public static int NumberOfWorkingDaysBetween2Dates(DateTime start,DateTime due,IEnumerable<DateTime> holidays) { var dic = new Dictionary<DateTime, DayOfWeek>(); var totalDays = (due - start).Days; for (int i = 0; i < totalDays + 1; i++) { if (!holidays.Any(x => x == start.AddDays(i))) dic.Add(start.AddDays(i), start.AddDays(i).DayOfWeek); } return dic.Where(x => x.Value != DayOfWeek.Saturday && x.Value != DayOfWeek.Sunday).Count(); }
Pada dasarnya saya ingin mengikuti setiap tanggal dan mengevaluasi kondisi saya:
tetapi juga saya ingin menghindari pengulangan tanggal.
Dengan menjalankan dan mengukur waktu yang dibutuhkan untuk mengevaluasi 1 tahun penuh, saya mendapatkan hasil sebagai berikut:
static void Main(string[] args) { var start = new DateTime(2017, 1, 1); var due = new DateTime(2017, 12, 31); var sw = Stopwatch.StartNew(); var days = NumberOfWorkingDaysBetween2Dates(start, due,NationalHolidays()); sw.Stop(); Console.WriteLine($"Total working days = {days} --- time: {sw.Elapsed}"); Console.ReadLine(); // result is: // Total working days = 249-- - time: 00:00:00.0269087 }
Edit: metode baru yang lebih sederhana:
public static int ToBusinessWorkingDays(this DateTime start, DateTime due, DateTime[] holidays) { return Enumerable.Range(0, (due - start).Days) .Select(a => start.AddDays(a)) .Where(a => a.DayOfWeek != DayOfWeek.Sunday) .Where(a => a.DayOfWeek != DayOfWeek.Saturday) .Count(a => !holidays.Any(x => x == a)); }
sumber
Saya pikir tidak ada jawaban di atas yang benar. Tak satu pun dari mereka menyelesaikan semua kasus khusus seperti ketika tanggal dimulai dan berakhir pada tengah akhir pekan, ketika tanggal dimulai pada hari Jumat dan berakhir pada Senin depan, dll. Selain itu, mereka semua membulatkan perhitungan menjadi keseluruhan hari, jadi jika tanggal mulai di tengah hari sabtu misalnya, itu akan mengurangi satu hari penuh dari hari kerja, memberikan hasil yang salah ...
Bagaimanapun, inilah solusi saya yang cukup efisien dan sederhana dan berfungsi untuk semua kasus. Triknya adalah menemukan hari Senin sebelumnya untuk tanggal mulai dan berakhir, dan kemudian lakukan kompensasi kecil ketika mulai dan akhir terjadi selama akhir pekan:
public double WorkDays(DateTime startDate, DateTime endDate){ double weekendDays; double days = endDate.Subtract(startDate).TotalDays; if(days<0) return 0; DateTime startMonday = startDate.AddDays(DayOfWeek.Monday - startDate.DayOfWeek).Date; DateTime endMonday = endDate.AddDays(DayOfWeek.Monday - endDate.DayOfWeek).Date; weekendDays = ((endMonday.Subtract(startMonday).TotalDays) / 7) * 2; // compute fractionary part of weekend days double diffStart = startDate.Subtract(startMonday).TotalDays - 5; double diffEnd = endDate.Subtract(endMonday).TotalDays - 5; // compensate weekenddays if(diffStart>0) weekendDays -= diffStart; if(diffEnd>0) weekendDays += diffEnd; return days - weekendDays; }
sumber
using System; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { DateTime start = new DateTime(2014, 1, 1); DateTime stop = new DateTime(2014, 12, 31); int totalWorkingDays = GetNumberOfWorkingDays(start, stop); Console.WriteLine("There are {0} working days.", totalWorkingDays); } private static int GetNumberOfWorkingDays(DateTime start, DateTime stop) { TimeSpan interval = stop - start; int totalWeek = interval.Days / 7; int totalWorkingDays = 5 * totalWeek; int remainingDays = interval.Days % 7; for (int i = 0; i <= remainingDays; i++) { DayOfWeek test = (DayOfWeek)(((int)start.DayOfWeek + i) % 7); if (test >= DayOfWeek.Monday && test <= DayOfWeek.Friday) totalWorkingDays++; } return totalWorkingDays; } } }
sumber
Bekerja dan tanpa loop
Metode ini tidak menggunakan loop apa pun dan sebenarnya cukup sederhana. Ini memperluas rentang tanggal menjadi minggu penuh karena kita tahu bahwa setiap minggu memiliki 5 hari kerja. Kemudian menggunakan tabel pencarian untuk menemukan jumlah hari kerja yang akan dikurangi dari awal dan akhir untuk mendapatkan hasil yang benar. Saya telah memperluas kalkulasi untuk membantu menunjukkan apa yang sedang terjadi, tetapi semuanya dapat diringkas menjadi satu baris jika diperlukan.
Bagaimanapun, ini berhasil untuk saya dan jadi saya pikir saya akan mempostingnya di sini seandainya itu dapat membantu orang lain. Selamat membuat kode.
Perhitungan
Budaya
Kode mengasumsikan hari kerja Senin sampai Jumat. Untuk budaya lain, seperti Minggu hingga Kamis, Anda harus mengimbangi tanggal sebelum penghitungan.
metode
public int Weekdays(DateTime min, DateTime max) { if (min.Date > max.Date) throw new Exception("Invalid date span"); var t = (max.AddDays(1).Date - min.Date).TotalDays; var a = (int) min.DayOfWeek; var b = 6 - (int) max.DayOfWeek; var k = 1.4; var m = new int[]{0, 0, 1, 2, 3, 4, 5}; var c = m[a] + m[b]; return (int)((t + a + b) / k) - c; }
sumber
Saya hanya akan membagikan solusi saya. Ini berhasil untuk saya, mungkin saya hanya tidak memperhatikan / tahu bahwa ada bug. Saya mulai dengan minggu pertama yang belum selesai jika ada. satu minggu penuh adalah dari hari Minggu hingga Sabtu, jadi jika (int) _now.DayOfWeek bukan 0 (Minggu), minggu pertama tidak lengkap.
Saya hanya mengurangi 1 hitungan minggu pertama untuk minggu pertama Sabtu lalu menambahkannya ke hitungan baru;
Kemudian saya mendapatkan minggu terakhir yang tidak lengkap, lalu kurangi 1 untuk minggu itu lalu tambahkan ke hitungan baru.
Kemudian akhirnya, jumlah minggu lengkap dikalikan dengan 5 (hari kerja) ditambahkan ke hitungan baru.
public int RemoveNonWorkingDays(int numberOfDays){ int workingDays = 0; int firstWeek = 7 - (int)_now.DayOfWeek; if(firstWeek < 7){ if(firstWeek > numberOfDays) return numberOfDays; workingDays += firstWeek-1; numberOfDays -= firstWeek; } int lastWeek = numberOfDays % 7; if(lastWeek > 0){ numberOfDays -= lastWeek; workingDays += lastWeek - 1; } workingDays += (numberOfDays/7)*5; return workingDays; }
sumber
Saya mengalami kesulitan menemukan versi TSQL yang solid dari kode ini. Di bawah ini pada dasarnya adalah konversi kode C # di sini dengan penambahan tabel Liburan yang harus digunakan untuk menghitung hari libur.
CREATE TABLE dbo.Holiday ( HolidayDt DATE NOT NULL, Name NVARCHAR(50) NOT NULL, IsWeekday BIT NOT NULL, CONSTRAINT PK_Holiday PRIMARY KEY (HolidayDt) ) GO CREATE INDEX IDX_Holiday ON Holiday (HolidayDt, IsWeekday) GO CREATE function dbo.GetBusinessDays ( @FirstDay datetime, @LastDay datetime ) RETURNS INT AS BEGIN DECLARE @BusinessDays INT, @FullWeekCount INT SELECT @FirstDay = CONVERT(DATETIME,CONVERT(DATE,@FirstDay)) , @LastDay = CONVERT(DATETIME,CONVERT(DATE,@LastDay)) IF @FirstDay > @LastDay RETURN NULL; SELECT @BusinessDays = DATEDIFF(DAY, @FirstDay, @LastDay) + 1 SELECT @FullWeekCount = @BusinessDays / 7; -- find out if there are weekends during the time exceedng the full weeks IF @BusinessDays > (@FullWeekCount * 7) BEGIN -- we are here to find out if there is a 1-day or 2-days weekend -- in the time interval remaining after subtracting the complete weeks DECLARE @firstDayOfWeek INT, @lastDayOfWeek INT; SELECT @firstDayOfWeek = DATEPART(DW, @FirstDay), @lastDayOfWeek = DATEPART(DW, @LastDay); IF @lastDayOfWeek < @firstDayOfWeek SELECT @lastDayOfWeek = @lastDayOfWeek + 7; IF @firstDayOfWeek <= 6 BEGIN IF (@lastDayOfWeek >= 7) --Both Saturday and Sunday are in the remaining time interval BEGIN SELECT @BusinessDays = @BusinessDays - 2 END ELSE IF @lastDayOfWeek>=6 --Only Saturday is in the remaining time interval BEGIN SELECT @BusinessDays = @BusinessDays - 1 END END ELSE IF @firstDayOfWeek <= 7 AND @lastDayOfWeek >=7 -- Only Sunday is in the remaining time interval BEGIN SELECT @BusinessDays = @BusinessDays - 1 END END -- subtract the weekends during the full weeks in the interval DECLARE @Holidays INT; SELECT @Holidays = COUNT(*) FROM Holiday WHERE HolidayDt BETWEEN @FirstDay AND @LastDay AND IsWeekday = CAST(1 AS BIT) SELECT @BusinessDays = @BusinessDays - (@FullWeekCount + @FullWeekCount) -- - @Holidays RETURN @BusinessDays END
sumber
int BusinessDayDifference(DateTime Date1, DateTime Date2) { int Sign = 1; if (Date2 > Date1) { Sign = -1; DateTime TempDate = Date1; Date1 = Date2; Date2 = TempDate; } int BusDayDiff = (int)(Date1.Date - Date2.Date).TotalDays; if (Date1.DayOfWeek == DayOfWeek.Saturday) BusDayDiff -= 1; if (Date2.DayOfWeek == DayOfWeek.Sunday) BusDayDiff -= 1; int Week1 = GetWeekNum(Date1); int Week2 = GetWeekNum(Date2); int WeekDiff = Week1 - Week2; BusDayDiff -= WeekDiff * 2; foreach (DateTime Holiday in Holidays) if (Date1 >= Holiday && Date2 <= Holiday) BusDayDiff--; BusDayDiff *= Sign; return BusDayDiff; } private int GetWeekNum(DateTime Date) { return (int)(Date.AddDays(-(int)Date.DayOfWeek).Ticks / TimeSpan.TicksPerDay / 7); }
sumber
Inilah satu solusi yang sangat sederhana untuk masalah ini. Kami memiliki tanggal mulai, tanggal akhir dan "for loop" untuk menambah hari dan menghitung untuk melihat apakah itu hari kerja atau akhir pekan dengan mengonversi ke string DayOfWeek.
class Program { static void Main(string[] args) { DateTime day = new DateTime(); Console.Write("Inser your end date (example: 01/30/2015): "); DateTime endDate = DateTime.Parse(Console.ReadLine()); int numberOfDays = 0; for (day = DateTime.Now.Date; day.Date < endDate.Date; day = day.Date.AddDays(1)) { string dayToString = Convert.ToString(day.DayOfWeek); if (dayToString != "Saturday" && dayToString != "Sunday") numberOfDays++; } Console.WriteLine("Number of working days (not including local holidays) between two dates is "+numberOfDays); } }
sumber
Berdasarkan komentar yang ditandai sebagai jawaban dan tambalan direkomendasikan, serta -> Versi ini ingin mengubah Hari ke Jam Kerja ... Mempertimbangkan jam pada hari yang sama juga.
/// <summary> /// Calculates number of business days, taking into account: /// - weekends (Saturdays and Sundays) /// - bank holidays in the middle of the week /// </summary> /// <param name="firstDay">First day in the time interval</param> /// <param name="lastDay">Last day in the time interval</param> /// <param name="bankHolidays">List of bank holidays excluding weekends</param> /// <returns>Number of business hours during the 'span'</returns> public static int BusinessHoursUntil(DateTime firstDay, DateTime lastDay, params DateTime[] bankHolidays) { var original_firstDay = firstDay; var original_lastDay = lastDay; firstDay = firstDay.Date; lastDay = lastDay.Date; if (firstDay > lastDay) return -1; //// throw new ArgumentException("Incorrect last day " + lastDay); TimeSpan span = lastDay - firstDay; int businessDays = span.Days + 1; int fullWeekCount = businessDays / 7; // find out if there are weekends during the time exceedng the full weeks if (businessDays > fullWeekCount * 7) { // we are here to find out if there is a 1-day or 2-days weekend // in the time interval remaining after subtracting the complete weeks int firstDayOfWeek = firstDay.DayOfWeek == DayOfWeek.Sunday ? 7 : (int)firstDay.DayOfWeek; int lastDayOfWeek = lastDay.DayOfWeek == DayOfWeek.Sunday ? 7 : (int)lastDay.DayOfWeek; if (lastDayOfWeek < firstDayOfWeek) lastDayOfWeek += 7; if (firstDayOfWeek <= 6) { if (lastDayOfWeek >= 7)// Both Saturday and Sunday are in the remaining time interval businessDays -= 2; else if (lastDayOfWeek >= 6)// Only Saturday is in the remaining time interval businessDays -= 1; } else if (firstDayOfWeek <= 7 && lastDayOfWeek >= 7)// Only Sunday is in the remaining time interval businessDays -= 1; } // subtract the weekends during the full weeks in the interval businessDays -= fullWeekCount + fullWeekCount; if (bankHolidays != null && bankHolidays.Any()) { // subtract the number of bank holidays during the time interval foreach (DateTime bankHoliday in bankHolidays) { DateTime bh = bankHoliday.Date; if (firstDay <= bh && bh <= lastDay) --businessDays; } } int total_business_hours = 0; if (firstDay.Date == lastDay.Date) {//If on the same day, go granular with Hours from the Orginial_*Day values total_business_hours = (int)(original_lastDay - original_firstDay).TotalHours; } else {//Convert Business-Days to TotalHours total_business_hours = (int)(firstDay.AddDays(businessDays).AddHours(firstDay.Hour) - firstDay).TotalHours; } return total_business_hours; }
sumber
Saya baru saja meningkatkan jawaban @Alexander dan @Slauma untuk mendukung minggu kerja sebagai parameter, untuk kasus di mana Sabtu adalah hari kerja, atau bahkan kasus di mana hanya ada beberapa hari dalam seminggu yang dianggap sebagai hari kerja:
/// <summary> /// Calculate the number of business days between two dates, considering: /// - Days of the week that are not considered business days. /// - Holidays between these two dates. /// </summary> /// <param name="fDay">First day of the desired 'span'.</param> /// <param name="lDay">Last day of the desired 'span'.</param> /// <param name="BusinessDaysOfWeek">Days of the week that are considered to be business days, if NULL considers monday, tuesday, wednesday, thursday and friday as business days of the week.</param> /// <param name="Holidays">Holidays, if NULL, considers no holiday.</param> /// <returns>Number of business days during the 'span'</returns> public static int BusinessDaysUntil(this DateTime fDay, DateTime lDay, DayOfWeek[] BusinessDaysOfWeek = null, DateTime[] Holidays = null) { if (BusinessDaysOfWeek == null) BusinessDaysOfWeek = new DayOfWeek[] { DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday }; if (Holidays == null) Holidays = new DateTime[] { }; fDay = fDay.Date; lDay = lDay.Date; if (fDay > lDay) throw new ArgumentException("Incorrect last day " + lDay); int bDays = (lDay - fDay).Days + 1; int fullWeekCount = bDays / 7; int fullWeekCountMult = 7 - WeekDays.Length; // Find out if there are weekends during the time exceedng the full weeks if (bDays > (fullWeekCount * 7)) { int fDayOfWeek = (int)fDay.DayOfWeek; int lDayOfWeek = (int)lDay.DayOfWeek; if (fDayOfWeek > lDayOfWeek) lDayOfWeek += 7; // If they are the same, we already covered it right before the Holiday subtraction if (lDayOfWeek != fDayOfWeek) { // Here we need to see if any of the days between are considered business days for (int i = fDayOfWeek; i <= lDayOfWeek; i++) if (!WeekDays.Contains((DayOfWeek)(i > 6 ? i - 7 : i))) bDays -= 1; } } // Subtract the days that are not in WeekDays[] during the full weeks in the interval bDays -= (fullWeekCount * fullWeekCountMult); // Subtract the number of bank holidays during the time interval bDays = bDays - Holidays.Select(x => x.Date).Count(x => fDay <= x && x <= lDay); return bDays; }
sumber
Berikut adalah fungsi yang dapat kita gunakan untuk menghitung hari kerja antara dua tanggal. Saya tidak menggunakan daftar hari libur karena dapat berbeda-beda di setiap negara / wilayah.
Jika kita ingin tetap menggunakannya, kita dapat mengambil argumen ketiga sebagai daftar hari libur dan sebelum menambah hitungan kita harus memeriksa bahwa daftar tidak mengandung d
public static int GetBussinessDaysBetweenTwoDates(DateTime StartDate, DateTime EndDate) { if (StartDate > EndDate) return -1; int bd = 0; for (DateTime d = StartDate; d < EndDate; d = d.AddDays(1)) { if (d.DayOfWeek != DayOfWeek.Saturday && d.DayOfWeek != DayOfWeek.Sunday) bd++; } return bd; }
sumber
Saya yakin ini bisa menjadi cara yang lebih sederhana:
public int BusinessDaysUntil(DateTime start, DateTime end, params DateTime[] bankHolidays) { int tld = (int)((end - start).TotalDays) + 1; //including end day int not_buss_day = 2 * (tld / 7); //Saturday and Sunday int rest = tld % 7; //rest. if (rest > 0) { int tmp = (int)start.DayOfWeek - 1 + rest; if (tmp == 6 || start.DayOfWeek == DayOfWeek.Sunday) not_buss_day++; else if (tmp > 6) not_buss_day += 2; } foreach (DateTime bankHoliday in bankHolidays) { DateTime bh = bankHoliday.Date; if (!(bh.DayOfWeek == DayOfWeek.Saturday || bh.DayOfWeek == DayOfWeek.Sunday) && (start <= bh && bh <= end)) { not_buss_day++; } } return tld - not_buss_day; }
sumber
Ini adalah ide lain - metode ini memungkinkan untuk menentukan minggu kerja dan hari libur.
Idenya di sini adalah kita menemukan inti rentang tanggal dari hari kerja pertama dalam seminggu hingga hari akhir pekan terakhir dalam seminggu. Hal ini memungkinkan kami menghitung seluruh minggu dengan mudah ( tanpa mengulang semua tanggal). Yang perlu kita lakukan selanjutnya adalah menambahkan hari kerja yang jatuh sebelum awal dan akhir rentang inti ini.
public static int CalculateWorkingDays( DateTime startDate, DateTime endDate, IList<DateTime> holidays, DayOfWeek firstDayOfWeek, DayOfWeek lastDayOfWeek) { // Make sure the defined working days run contiguously if (lastDayOfWeek < firstDayOfWeek) { throw new Exception("Last day of week cannot fall before first day of week!"); } // Create a list of the days of the week that make-up the weekend by working back // from the firstDayOfWeek and forward from lastDayOfWeek to get the start and end // the weekend var weekendStart = lastDayOfWeek == DayOfWeek.Saturday ? DayOfWeek.Sunday : lastDayOfWeek + 1; var weekendEnd = firstDayOfWeek == DayOfWeek.Sunday ? DayOfWeek.Saturday : firstDayOfWeek - 1; var weekendDays = new List<DayOfWeek>(); var w = weekendStart; do { weekendDays.Add(w); if (w == weekendEnd) break; w = (w == DayOfWeek.Saturday) ? DayOfWeek.Sunday : w + 1; } while (true); // Force simple dates - no time startDate = startDate.Date; endDate = endDate.Date; // Ensure a progessive date range if (endDate < startDate) { var t = startDate; startDate = endDate; endDate = t; } // setup some working variables and constants const int daysInWeek = 7; // yeah - really! var actualStartDate = startDate; // this will end up on startOfWeek boundary var actualEndDate = endDate; // this will end up on weekendEnd boundary int workingDaysInWeek = daysInWeek - weekendDays.Count; int workingDays = 0; // the result we are trying to find int leadingDays = 0; // the number of working days leading up to the firstDayOfWeek boundary int trailingDays = 0; // the number of working days counting back to the weekendEnd boundary // Calculate leading working days // if we aren't on the firstDayOfWeek we need to step forward to the nearest if (startDate.DayOfWeek != firstDayOfWeek) { var d = startDate; do { if (d.DayOfWeek == firstDayOfWeek || d >= endDate) { actualStartDate = d; break; } if (!weekendDays.Contains(d.DayOfWeek)) { leadingDays++; } d = d.AddDays(1); } while(true); } // Calculate trailing working days // if we aren't on the weekendEnd we step back to the nearest if (endDate >= actualStartDate && endDate.DayOfWeek != weekendEnd) { var d = endDate; do { if (d.DayOfWeek == weekendEnd || d < actualStartDate) { actualEndDate = d; break; } if (!weekendDays.Contains(d.DayOfWeek)) { trailingDays++; } d = d.AddDays(-1); } while(true); } // Calculate the inclusive number of days between the actualStartDate and the actualEndDate var coreDays = (actualEndDate - actualStartDate).Days + 1; var noWeeks = coreDays / daysInWeek; // add together leading, core and trailing days workingDays += noWeeks * workingDaysInWeek; workingDays += leadingDays; workingDays += trailingDays; // Finally remove any holidays that fall within the range. if (holidays != null) { workingDays -= holidays.Count(h => h >= startDate && (h <= endDate)); } return workingDays; }
sumber
Karena saya tidak bisa berkomentar. Ada satu masalah lagi dengan solusi yang diterima di mana hari libur bank dikurangi bahkan ketika mereka berada di akhir pekan. Melihat bagaimana masukan lain dicentang, ini juga cocok.
Oleh karena itu, foreach harus:
// subtract the number of bank holidays during the time interval foreach (DateTime bankHoliday in bankHolidays) { DateTime bh = bankHoliday.Date; // Do not subtract bank holidays when they fall in the weekend to avoid double subtraction if (bh.DayOfWeek == DayOfWeek.Saturday || bh.DayOfWeek == DayOfWeek.Sunday) continue; if (firstDay <= bh && bh <= lastDay) --businessDays; }
sumber
Berikut adalah pendekatan jika Anda menggunakan MVC. Saya juga telah menghitung hari libur nasional atau hari raya apa pun yang akan dikecualikan dengan mengambilnya dari kalender hari libur yang akan Anda perlukan untuk membuatnya.
foreach (DateTime day in EachDay(model)) { bool key = false; foreach (LeaveModel ln in holidaycalendar) { if (day.Date == ln.Date && day.DayOfWeek != DayOfWeek.Saturday && day.DayOfWeek != DayOfWeek.Sunday) { key = true; break; } } if (day.DayOfWeek == DayOfWeek.Saturday || day.DayOfWeek == DayOfWeek.Sunday) { key = true; } if (key != true) { leavecount++; } }
Leavemodel adalah daftar di sini
sumber
Berikut adalah fungsi pembantu yang saya tulis untuk tugas itu.
itu juga mengembalikan hitungan akhir pekan melalui
out
parameter.jika Anda ingin, Anda dapat menyesuaikan hari "akhir pekan" dalam waktu proses untuk negara-negara yang menggunakan hari akhir pekan yang berbeda atau untuk menyertakan hari libur melalui
weekendDays[]
parameter opsional:public static int GetNetworkDays(DateTime startDate, DateTime endDate,out int totalWeekenDays, DayOfWeek[] weekendDays = null) { if (startDate >= endDate) { throw new Exception("start date can not be greater then or equel to end date"); } DayOfWeek[] weekends = new DayOfWeek[] { DayOfWeek.Sunday, DayOfWeek.Saturday }; if (weekendDays != null) { weekends = weekendDays; } var totaldays = (endDate - startDate).TotalDays + 1; // add one to include the first day to var counter = 0; var workdaysCounter = 0; var weekendsCounter = 0; for (int i = 0; i < totaldays; i++) { if (weekends.Contains(startDate.AddDays(counter).DayOfWeek)) { weekendsCounter++; } else { workdaysCounter++; } counter++; } totalWeekenDays = weekendsCounter; return workdaysCounter; }
sumber
Saya datang dengan solusi berikut
var dateStart = new DateTime(2019,01,10); var dateEnd = new DateTime(2019,01,31); var timeBetween = (dateEnd - dateStart).TotalDays + 1; int numberOf7DayWeeks = (int)(timeBetween / 7); int numberOfWeekendDays = numberOf7DayWeeks * 2; int workingDays =(int)( timeBetween - numberOfWeekendDays); if(dateStart.DayOfWeek == DayOfWeek.Saturday || dateEnd.DayOfWeek == DayOfWeek.Sunday){ workingDays -=2; } if(dateStart.DayOfWeek == DayOfWeek.Sunday || dateEnd.DayOfWeek == DayOfWeek.Saturday){ workingDays -=1; }
sumber
Anda hanya perlu mengulang setiap hari dalam rentang waktu dan mengurangi hari dari konter jika itu hari Sabtu atau Minggu.
private float SubtractWeekend(DateTime start, DateTime end) { float totaldays = (end.Date - start.Date).Days; var iterationVal = totalDays; for (int i = 0; i <= iterationVal; i++) { int dayVal = (int)start.Date.AddDays(i).DayOfWeek; if(dayVal == 6 || dayVal == 0) { // saturday or sunday totalDays--; } } return totalDays; }
sumber
public static int CalculateBusinessDaysInRange(this DateTime startDate, DateTime endDate, params DateTime[] holidayDates) { endDate = endDate.Date; if(startDate > endDate) throw new ArgumentException("The end date can not be before the start date!", nameof(endDate)); int accumulator = 0; DateTime itterator = startDate.Date; do { if(itterator.DayOfWeek != DayOfWeek.Saturday && itterator.DayOfWeek != DayOfWeek.Sunday && !holidayDates.Any(hol => hol.Date == itterator)) { accumulator++; } } while((itterator = itterator.AddDays(1)).Date <= endDate); return accumulator }
Saya hanya memposting ini karena terlepas dari semua jawaban luar biasa yang telah diberikan, tidak ada matematika yang masuk akal bagi saya. Ini jelas merupakan metode KISS yang seharusnya berhasil dan dapat dipelihara dengan baik. Memang, jika Anda menghitung rentang yang lebih besar dari 2-3 bulan, ini bukan cara yang paling efektif. Kami hanya menentukan apakah itu hari Sabtu atau Minggu atau tanggal tersebut adalah tanggal hari libur tertentu. Kalau belum kita tambahkan hari kerja. Jika sudah maka semuanya baik-baik saja.
Saya yakin ini bisa lebih disederhanakan dengan LINQ, tapi cara ini jauh lebih mudah untuk dipahami.
sumber
Namun pendekatan lain untuk menghitung hari kerja, tidak mempertimbangkan hari libur, tetapi dengan memperhitungkan waktu pengembalian jumlah hari:
public static double GetBusinessDays(DateTime startD, DateTime endD) { while (IsWeekend(startD)) startD = startD.Date.AddDays(1); while (IsWeekend(endD)) endD = endD.Date.AddDays(-1); var bussDays = (endD - startD).TotalDays - (2 * ((int)(endD - startD).TotalDays / 7)) - (startD.DayOfWeek > endD.DayOfWeek ? 2 : 0); return bussDays; } public static bool IsWeekend(DateTime d) { return d.DayOfWeek == DayOfWeek.Saturday || d.DayOfWeek == DayOfWeek.Sunday; }
Anda dapat bermain-main dengannya di sini: https://rextester.com/ASHRS53997
sumber
Ini adalah solusi umum.
startdayvalue adalah nomor hari dari tanggal mulai.
weekendday_1 adalah hari jumlah akhir minggu.
nomor hari - MON - 1, SEL - 2, ... SAB - 6, SUN -7.
perbedaan adalah perbedaan antara dua tanggal ..
Contoh: Tanggal Mulai: 4 April 2013, Tanggal Berakhir: 14 April 2013
Selisih: 10, startdayvalue: 4, weekendday_1: 7 (jika MINGGU adalah akhir pekan untuk Anda.)
Ini akan memberi Anda jumlah hari libur.
Jumlah hari kerja = (Selisih + 1) - hari libur1
if (startdayvalue > weekendday_1) { if (difference > ((7 - startdayvalue) + weekendday_1)) { holiday1 = (difference - ((7 - startdayvalue) + weekendday_1)) / 7; holiday1 = holiday1 + 1; } else { holiday1 = 0; } } else if (startdayvalue < weekendday_1) { if (difference > (weekendday_1 - startdayvalue)) { holiday1 = (difference - (weekendday_1 - startdayvalue)) / 7; holiday1 = holiday1 + 1; } else if (difference == (weekendday_1 - startdayvalue)) { holiday1 = 1; } else { holiday1 = 0; } } else { holiday1 = difference / 7; holiday1 = holiday1 + 1; }
sumber