un elenco fisso di date è un modo un po 'limitato per esprimere vacanze.
Si consideri che l'elenco contiene solo le date per l'anno corrente, quindi se oggi è il 30 dicembre 2015, la prossima festività sarà il 1 ° gennaio 2016 - che non si troverà nell'elenco.
Considerate inoltre che molte festività non cadono ogni anno nella stessa data. Spesso sono legati al giorno della settimana e talvolta sono determinati da calendari religiosi o arbitrariamente.
Un sistema più robusto dovrebbe gestire una varietà di diversi tipi di vacanze.Ecco una possibile implementazione:
public abstract class Holiday
{
public abstract DateTime? GetDate(int year);
}
public class MonthDayBasedHoliday : Holiday
{
private readonly int _month;
private readonly int _day;
public MonthDayBasedHoliday(int month, int day)
{
_month = month;
_day = day;
}
public override DateTime? GetDate(int year)
{
return new DateTime(year, _month, _day);
}
}
public class DayOfWeekBasedHoliday : Holiday
{
private readonly int _occurrence;
private readonly DayOfWeek _dayOfWeek;
private readonly int _month;
public DayOfWeekBasedHoliday(int occurrence, DayOfWeek dayOfWeek, int month)
{
_occurrence = occurrence;
_dayOfWeek = dayOfWeek;
_month = month;
}
public override DateTime? GetDate(int year)
{
if (_occurrence <= 4)
{
DateTime dt = new DateTime(year, _month, 1);
int delta = (_dayOfWeek - dt.DayOfWeek + 7) % 7;
delta += 7 * (_occurrence - 1);
return dt.AddDays(delta);
}
else // last occurrence in month
{
int daysInMonth = DateTime.DaysInMonth(year, _month);
DateTime dt = new DateTime(year, _month, daysInMonth);
int delta = (dt.DayOfWeek - _dayOfWeek + 7) % 7;
return dt.AddDays(-delta);
}
}
}
public class FixedDateBasedHoliday : Holiday
{
private readonly IDictionary<int, DateTime> _dates;
public FixedDateBasedHoliday(params DateTime[] dates)
{
_dates = dates.ToDictionary(x => x.Year, x => x);
}
public override DateTime? GetDate(int year)
{
if (_dates.ContainsKey(year))
return _dates[year];
// fixed date not established for year
return null;
}
}
Con queste definito, possiamo ora definire vacanze molto più robusta, come ad esempio:
var holidays = new List<Holiday>();
// New Year's Day
holidays.Add(new MonthDayBasedHoliday(1, 1));
// President's Day (US)
holidays.Add(new DayOfWeekBasedHoliday(3, DayOfWeek.Monday, 2));
// Easter (Western Observance)
holidays.Add(new FixedDateBasedHoliday(new DateTime(2015, 4, 5), new DateTime(2016, 3, 27)));
// Memorial Day (US)
holidays.Add(new DayOfWeekBasedHoliday(5, DayOfWeek.Monday, 5));
// Christmas Day
holidays.Add(new MonthDayBasedHoliday(12, 25));
E ora, siamo in grado di creare un metodo che verifica il giorno lavorativo successivo , come da voi richiesto:
public static DateTime GetNextNonHolidayWeekDay(DateTime date, IList<Holiday> holidays, IList<DayOfWeek> weekendDays)
{
// always start with tomorrow, and truncate time to be safe
date = date.Date.AddDays(1);
// calculate holidays for both this year and next year
var holidayDates = holidays.Select(x => x.GetDate(date.Year))
.Union(holidays.Select(x => x.GetDate(date.Year + 1)))
.Where(x=> x != null)
.Select(x=> x.Value)
.OrderBy(x => x).ToArray();
// increment until we get a non-weekend and non-holiday date
while (true)
{
if (weekendDays.Contains(date.DayOfWeek) || holidayDates.Contains(date))
date = date.AddDays(1);
else
return date;
}
}
Questo metodo potrebbe andare in astratto Holiday
di classe, o potrebbe andare da nessuna parte davvero.
Esempio di utilizzo (con sopra definizione per holidays
):
var weekendDays = new[] { DayOfWeek.Saturday, DayOfWeek.Sunday };
DateTime workDay = GetNextNonHolidayWeekDay(new DateTime(2015, 12, 31), holidays, weekendDays);
// returns 2016-01-04
Questo non è ancora abbastanza una soluzione completa però. Molte vacanze hanno regole di calcolo più complesse. Come esercizio lasciato al lettore, prova a implementare una classe che derivi da Holiday
per il secondo giorno nella festa del Ringraziamento negli Stati Uniti. Il primo giorno cadrà sempre il 4 ° giovedì di novembre, ma il secondo giorno è sempre "il venerdì successivo al 4 ° di novembre", piuttosto che "il 4 ° venerdì di novembre" (vedi novembre 2019 per un esempio di dove questo questioni).
+1 e facendo 'IsHoliday' una funzione si può evitare la costruzione di un elenco statico di vacanze e, invece di calcolare al volo per la data/l'anno passato - che è importante per le date come Pasqua che si muovono. –
Che cos'è 'IsHolliday (data)'? Esiste questo metodo? – Fabjan
@Fabjan Devi implementare questo metodo tu stesso. Ad esempio è possibile inserire il codice scritto dall'OP per verificare se una data è al momento. – fjardon