J'ai une exigence où je dois travailler sur un champ de date, donc l'exigence est quelque chose comme ça
J'appellerai le champ comme date minimale possible
Ajouter +1 à la date
Si la date minimale possible tombe un week-end (samedi ou dimanche) après l’ajout d’un jour, affiche le jour ouvrable suivant le lundi
Si la date minimale possible tombe un jour férié, affichez le jour ouvrable suivant. (Jours fériés 1.1, 1.5, 3.10, 25.12, 26.12)
Si la date minimale possible tombe un week-end (samedi ou dimanche) après avoir ajouté 1 jour et que le jour suivant soit un jour férié, affichez le jour ouvrable suivant. Exemple: après +1 jour si le jour minimum possible est le samedi, nous devrons afficher le lundi. Mais si lundi est un jour férié, nous devons afficher mardi.
J'ai essayé une solution au problème ci-dessus en ayant plusieurs cas de figure, mais je me demandais simplement s'il existait un moyen générique et gracieux de le faire.
J'ai essayé
var Holidays = new List<DateTime>();
Holidays.Add(new DateTime(DateTime.Now.Year,1,1));
Holidays.Add(new DateTime(DateTime.Now.Year,1,5));
Holidays.Add(new DateTime(DateTime.Now.Year,3,10));
Holidays.Add(new DateTime(DateTime.Now.Year,12,25));
if(date.DayOfWeek === DayOfWeek.Saturday || date.DayOfWeek === DayOfWeek.Sunday)
{
//Logic to add +1 and again some logic to check for weekends and weekdays
}
else if(holidays.Contain(date))
{
//Logic to add +1 and again some logic to check for weekends and weekdays
}
En gros, vous voulez avoir le prochain jour ouvrable. Donc, vous pouvez boucler sur cette condition en ajoutant 1 jour à la date actuelle
do {
date = date.AddDays(1);
} while(IsHolliday(date) || IsWeekEnd(date));
Dans le code précédent, IsHolliday
est un prédicat indiquant si une date est holliday. Par exemple, en réutilisant sans vergogne votre code:
class Program
{
private static readonly HashSet<DateTime> Holidays = new HashSet<DateTime>();
private static bool IsHoliday(DateTime date)
{
return Holidays.Contains(date);
}
private static bool IsWeekEnd(DateTime date)
{
return date.DayOfWeek == DayOfWeek.Saturday
|| date.DayOfWeek == DayOfWeek.Sunday;
}
private static DateTime GetNextWorkingDay(DateTime date)
{
do
{
date = date.AddDays(1);
} while (IsHoliday(date) || IsWeekEnd(date));
return date;
}
static void Main(string[] args)
{
Holidays.Add(new DateTime(DateTime.Now.Year, 1, 1));
Holidays.Add(new DateTime(DateTime.Now.Year, 1, 5));
Holidays.Add(new DateTime(DateTime.Now.Year, 3, 10));
Holidays.Add(new DateTime(DateTime.Now.Year, 12, 25));
var dt = GetNextWorkingDay(DateTime.Parse(@"2015-10-31"));
Console.WriteLine(dt);
Console.ReadKey();
}
}
Une liste fixe de dates est un moyen quelque peu limité d’exprimer des jours fériés.
Considérez que votre liste ne contient que des dates pour l'année en cours. Par conséquent, si nous sommes le 30 décembre 2015, les prochaines vacances seraient le 1er janvier 2016 - ce que vous ne trouverez pas dans votre liste.
Considérez également que de nombreux jours fériés ne tombent pas à la même date chaque année. Souvent, ils sont liés au jour de la semaine et parfois, ils sont déterminés par des calendriers religieux ou arbitrairement.
Un système plus robuste devrait gérer une variété de types de congés différents. Voici une implémentation possible:
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;
}
}
Avec ceux-ci définis, nous pouvons maintenant définir des vacances beaucoup plus robustes, telles que:
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));
Et maintenant, nous pouvons créer une méthode qui vérifie le jour ouvrable suivant, comme vous l'avez demandé:
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;
}
}
Cette méthode peut aller dans la classe abstraite Holiday
ou peut aller n'importe où vraiment.
Exemple d'utilisation (avec la définition ci-dessus pour holidays
):
var weekendDays = new[] { DayOfWeek.Saturday, DayOfWeek.Sunday };
DateTime workDay = GetNextNonHolidayWeekDay(new DateTime(2015, 12, 31), holidays, weekendDays);
// returns 2016-01-04
Ce n’est toujours pas une solution complète. De nombreuses vacances ont des règles de calcul plus complexes. En tant qu’exercice laissé au lecteur, essayez d’implémenter une classe dérivée de Holiday
pour le deuxième jour des vacances de Thanksgiving aux États-Unis. Le premier jour tombe toujours le 4ème jeudi de novembre, mais le deuxième jour est toujours "le vendredi suivant le 4ème jeudi de novembre", plutôt que le "4ème vendredi de novembre" (voir novembre 2019 pour un exemple questions).
D'après la réponse de @fjardon, vous pouvez utiliser Nager.Date il contient la logique de week-end de différents pays et contient des jours fériés pour plus de 70 pays.
nuget
PM> install-package Nager.Date
Extrait de code
//usings
using Nager.Date;
using Nager.Date.Extensions;
//logic
var date = DateTime.Today; //Set start date
var countryCode = CountryCode.US; //Set country
do
{
date = date.AddDays(1);
} while (DateSystem.IsPublicHoliday(date, countryCode) || date.IsWeekend(countryCode));
var Holidays = new List<DateTime>();
Holidays.Add(new DateTime(DateTime.Now.Year, 1, 1));
Holidays.Add(new DateTime(DateTime.Now.Year, 1, 5));
Holidays.Add(new DateTime(DateTime.Now.Year, 3, 10));
Holidays.Add(new DateTime(DateTime.Now.Year, 12, 25));
var exclude = new List<DayOfWeek> {DayOfWeek.Saturday, DayOfWeek.Sunday};
var targetDate = new DateTime(2015, 12, 24);
var myDate = Enumerable.Range(1, 30)
.Select( i => targetDate.AddDays(i) )
.First(a =>
!( exclude.Contains( a.DayOfWeek ) ||
Holidays.Contains(a)) );