ASP.NET C# - Funzioni DateTime per aggiungere, sottrarre e calcolare i giorni lavorativi
L'articolo di oggi nasce da un'esigenza particolare che ho avuto oggi in ufficio, legata a una serie di operazioni particolari da effettuare con le date: in particolare, avevo la necessità di confrontare un oggetto DateTime ricavato da una data inserita da un utente con data odierna, dopo aver incrementato quest'ultima di due giorni lavorativi.
Inizialmente ho pensato che, per risolvere il problema, sarebbe stato sufficiente escludere i sabati e le domeniche: tuttavia, mi sono ben presto reso conto di aver sottovalutato il problema, in quanto una simile soluzione non prende in considerazione una serie di festività nazionali, internazionali e addirittura cittadine (ad es. le feste dei patroni di Roma, Milano etc.). In buona sostanza, oltre a tutti i sabati e le domeniche dovevo gestire anche:
Le festività internazionali (o perlomeno quelle valide in occidente, come ad es. natale e capodanno).
Le festività calcolate (Pasqua e Pasquetta).
Le festività nazionali (come ad esempio il 25 aprile in Italia o il 4 luglio negli Stati Uniti d'America).
Le festività cittadine (come S. Pietro e Paolo a Roma, che cade il 29 giugno, o S. Ambrogio a Milano, che cade il 7 dicembre).
Qualsiasi altra festività o altro giorno non lavorativo personalizzato (ad es. i giorni di chiusura aziendale).
Tutti questi ragionamenti mi hanno portato a sviluppare una serie di metodi helper che, pur se non eccezionalmente eleganti, consentono di gestire in modo efficace tutte queste casistiche. Ho deciso di pubblicare il codice sorgente relativo all'attività all'interno di questo articolo, nella speranza che possano tornare utili anche a qualche altro sviluppatore.
Come si può vedere, i metodi principali (AddBusinessDays, SubtractBusinessDays and GetBusinessDays) possono essere usati come normali metodi helper statici o come extension method.
///
/// Helper/extension class for manipulating date and time values.
///
public static class DateTimeExtensions
{
///
/// Adds the given number of business days to the .
///
/// The date to be changed.
/// Number of business days to be added.
/// An optional list of holiday (non-business) days to consider.
/// A increased by a given number of business days.
public static DateTime AddBusinessDays(
this DateTime current,
int days,
IEnumerable holidays = null)
{
var sign = Math.Sign(days);
var unsignedDays = Math.Abs(days);
for (var i = 0; i {
do
{
current = current.AddDays(sign);
}
while (current.DayOfWeek == DayOfWeek.Saturday
|| current.DayOfWeek == DayOfWeek.Sunday
|| (holidays != null && holidays.Contains(current.Date))
);
}
return current;
}
///
/// Subtracts the given number of business days to the .
///
/// The date to be changed.
/// Number of business days to be subtracted.
/// An optional list of holiday (non-business) days to consider.
/// A increased by a given number of business days.
public static DateTime SubtractBusinessDays(
this DateTime current,
int days,
IEnumerable holidays)
{
return AddBusinessDays(current, -days, holidays);
}
///
/// Retrieves the number of business days from two dates
///
/// The inclusive start date
/// The inclusive end date
/// An optional list of holiday (non-business) days to consider.
///
public static int GetBusinessDays(
this DateTime startDate,
DateTime endDate,
IEnumerable holidays)
{
int cnt = 0;
for (var current = startDate; current {
do
{
cnt++;
}
while (current.DayOfWeek == DayOfWeek.Saturday
|| current.DayOfWeek == DayOfWeek.Sunday
|| (holidays != null && holidays.Contains(current.Date))
);
}
return cnt;
}
///
/// Calculate Easter Sunday for any given year.
/// src.: https://stackoverflow.com/a/2510411/1233379
///
/// The year to calcolate Easter against.
/// a DateTime object containing the Easter month and day for the given year
public static DateTime GetEasterSunday(int year)
{
int day = 0;
int month = 0;
int g = year % 19;
int c = year / 100;
int h = (c - (int)(c / 4) - (int)((8 * c + 13) / 25) + 19 * g + 15) % 30;
int i = h - (int)(h / 28) * (1 - (int)(h / 28) * (int)(29 / (h + 1)) * (int)((21 - g) / 11));
day = i - ((year + (int)(year / 4) + i + 2 - c + (int)(c / 4)) % 7) + 28;
month = 3;
if (day > 31)
{
month++;
day -= 31;
}
return new DateTime(year, month, day);
}
///
/// Retrieve holidays for given years
///
/// an array of years to retrieve the holidays
/// a country two letter ISO (ex.: "IT") to add the holidays specific for that country
/// a city name to add the holidays specific for that city
///
public static IEnumerable GetHolidays(int years, string countryCode = null, string cityName = null)
{
var lst = new List();
foreach (var year in years)
{
lst.AddRange(new {
new DateTime(year, 1, 1), // 1 gennaio (capodanno)
new DateTime(year, 1, 6), // 6 gennaio (epifania)
new DateTime(year, 6, 1), // 1 maggio (lavoro)
new DateTime(year, 8, 15), // 15 agosto (ferragosto)
new DateTime(year, 11, 1), // 1 novembre (ognissanti)
new DateTime(year, 12, 8), // 8 dicembre (immacolata concezione)
new DateTime(year, 12, 25), // 25 dicembre (natale)
new DateTime(year, 12, 26) // 26 dicembre (s. stefano)
});
// add easter sunday (pasqua) and monday (pasquetta)
var easterDate = GetEasterSunday(year);
lst.Add(easterDate);
lst.Add(easterDate.AddDays(1));
// country-specific holidays
if (!String.IsNullOrEmpty(countryCode))
{
switch (countryCode.ToUpper())
{
case "IT":
lst.Add(new DateTime(year, 4, 25)); // 25 aprile (liberazione)
break;
case "US":
lst.Add(new DateTime(year, 7, 4)); // 4 luglio (Independence Day)
break;
// todo: add other countries
default:
// unsupported country: do nothing
break;
}
}
// city-specific holidays
if (!String.IsNullOrEmpty(cityName))
{
switch (cityName)
{
case "Rome":
case "Roma":
lst.Add(new DateTime(year, 6, 29)); // 29 giugno (s. pietro e paolo)
break;
case "Milano":
case "Milan":
lst.Add(new DateTime(year, 12, 7)); // 7 dicembre (s. ambrogio)
break;
// todo: add other cities
default:
// unsupported city: do nothing
break;
}
}
}
return lst;
}
}
Il codice sorgente dovrebbe essere abbastanza chiaro: ad ogni buon conto, ecco una serie di esempi che spiegano come è possibile utilizzarlo.
Aggiungere 10 giorni lavorativi (saltando solo i sabati e le domeniche)
var dtResult = DateTimeUtil.AddBusinessDays(srcDate, 10);
Aggiungere 10 giorni lavorativi (saltando i sabati, le domeniche e le feste internazionali previste per il 2019)
var dtResult = DateTimeUtil.AddBusinessDays(srcDate, 10, GetHolidays(2019));
Aggiungere 10 giorni lavorativi (saltando i sabati, le domeniche e le feste internazionali e nazionali italiane previste per il 2019)
var dtResult = DateTimeUtil.AddBusinessDays(srcDate, 10, GetHolidays(2019, "IT"));
Aggiungere 10 giorni lavorativi (saltando i sabati, le domeniche, le feste internazionali, nazionali italiane e della città di Roma previste per il 2019)
var dtResult = DateTimeUtil.AddBusinessDays(srcDate, 10, GetHolidays(2019, "IT", "Rome"));
Per il momento è tutto: qualora queste classi vi siano utili, o se avete bisogno di ulteriori informazioni, sentitevi liberi di aggiungere il vostro commento in fondo a questo articolo. Alla prossima, e... felice sviluppo!
Â
Â
Read the full article