web-dev-qa-db-fra.com

Comment obtenir le prochain jour ouvrable, week-ends et jours fériés exclus

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

  1. Ajouter +1 à la date 

  2. 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

  3. 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)

  4. 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
}
11
CSharped

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();
    }
}
21
fjardon

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).

5
Matt Johnson

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));
3
Tino Hager
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)) );
0
Cetin Basoz