2015-10-27 14 views
10

Ho un requisito in cui ho bisogno di lavorare su un campo data, in modo che il requisito è qualche cosa come questaCome ottenere il giorno lavorativo successivo, esclusi i fine settimana e festivi

chiamerò il campo come minima possibile data

  1. Aggiungi +1 alla data

  2. Se la data minima possibile capita di cadere in un fine settimana (sabato o domenica) dopo l'aggiunta di 1 giorno, Visualizzare il giorno lavorativo successivo cioè Lunedi

  3. Se la data minima possibile si verifica in una vacanza, visualizzare il giorno lavorativo successivo. (Vacanze 1.1, 1.5, 3.10, 25.12, 26.12)

  4. Se la data minima possibile si verifica in un fine settimana (sabato o domenica) dopo l'aggiunta di 1 giorno e il giorno successivo è una festività, quindi mostrare la successiva giorno lavorativo. Ad esempio: dopo 1 giorno se il giorno minimo è sabato, dovremo visualizzare lunedì. Ma se il lunedì sembra essere una festa, dobbiamo mostrare martedì.

ho cercato una soluzione al problema di cui sopra avendo più se e gli altri casi, ma chiedo solo se non v'è alcun modo generico e grazioso di farlo?

ho cercato

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 
} 

risposta

17

In sostanza si vuole ottenere il giorno lavorativo successivo. Così si potrebbe ciclo a questa condizione aggiunta di 1 giorno per la data corrente

do { 
    date = date.AddDays(1); 
} while(IsHolliday(date) || IsWeekEnd(date)); 

Nel codice precedente IsHolliday è un predicato dice se una data è Holliday. Per esempio, spudoratamente riutilizzare il codice:

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(); 
    } 
} 
+0

+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. –

+0

Che cos'è 'IsHolliday (data)'? Esiste questo metodo? – Fabjan

+0

@Fabjan Devi implementare questo metodo tu stesso. Ad esempio è possibile inserire il codice scritto dall'OP per verificare se una data è al momento. – fjardon

0
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))); 
+1

Dovresti avviare 'datesAhead' dal' targetDate' invece della data corrente. Quindi non c'è bisogno di 'a> targetDate'. – juharr

+0

Hai ragione. Non ho nemmeno bisogno di 100 giorni. –

+0

Dal momento che si limiterà a iterare fino a raggiungere una corrispondenza, il conteggio passato a "Enumerable.Range" deve essere solo maggiore del numero massimo di giorni fino al giorno lavorativo successivo. La mia ipotesi è 5 o 6, ma ingrandirla non ha un effetto negativo. – juharr

4

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

+0

Il modo in cui hai modellato le vacanze sembra molto appropriato. Questa è un'ottima risposta. –

1

Sulla base della risposta da @fjardon, è possibile utilizzare Nager.Date contiene logica Weekend di diversi countrys e contiene giorni festivi per più di 70 paesi.

NuGet

PM> install-package Nager.Date 

Snippet di codice

//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)); 
Problemi correlati