2015-10-27 10 views
10

Ich habe eine Verpflichtung, wo ich auf einem Datumsfeld arbeiten muß, so ist die Forderung, soetwas wie dieseWie am nächsten Werktag erhalten, an Wochenenden und Feiertage ausgenommen

ich das Feld als Minimum nennt möglich Datum

  1. +1 bis zum Tag hinzufügen

  2. Wenn der kleinstmögliche Zeitpunkt geschieht nach dem 1. Zugabe Tag an einem Wochenende (Sa oder So) fällt, Anzeige des nächsten Arbeitstag dh Montag

  3. Wenn das minimal mögliche Datum auf einen Feiertag fällt, wird der nächste Arbeitstag angezeigt. (Feiertage 1.1, 1.5, 3.10, 25.12, 26.12)

  4. Wenn das Minimum mögliche Datum fällt auf ein Wochenende (Sa oder So) nach dem Hinzufügen von 1 Tag, und der Tag danach ist ein Feiertag, dann zeigen Sie die nächste Arbeitstag. ZB: Nach einem Tag, wenn der Mindesttag Samstag ist, müssen wir Montag anzeigen. Aber wenn Montag ein Feiertag ist, müssen wir Dienstag anzeigen.

Ich habe eine Lösung für das obige Problem versucht, durch mehr, wenn und sonst Fälle zu haben, aber nur fragen, ob es es zu tun jede generische und anmutige Art und Weise ist?

ich versucht habe

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 
} 

Antwort

17

Grundsätzlich möchte man am nächsten Werktag bekommen. So könnte man Schleife unter dieser Bedingung der Zugabe von 1 Tag bis zum aktuellen Datum

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

Im vorherigen Code IsHolliday ist ein Prädikat zu sagen, wenn ein Datum Holliday ist. Zum Beispiel der Wiederverwendung schamlos 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(); 
    } 
} 
+0

+1 und indem 'eine Funktion IsHoliday' Sie vermeiden können, eine statische Liste von Feiertagen zu bauen und stattdessen on the fly berechnen das Datum/Jahr ist vergangen - das ist wichtig für Daten wie Ostern, die sich bewegen. –

+0

Was ist 'IsHolliday (Datum)'? Gibt es diese Methode? – Fabjan

+0

@Fabjan Sie müssen diese Methode selbst implementieren. Zum Beispiel können Sie den Code eingeben, den das OP geschrieben hat, um zu prüfen, ob ein Datum in den Ferien ist. – 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

Sie sollten "datesAhead" von "targetDate" anstelle des aktuellen Datums starten. Dann ist das 'a> targetDate' nicht nötig. – juharr

+0

Sie haben Recht. Ich brauche auch nicht einmal 100 Tage. –

+0

Nun, da es nur iteriert wird, bis es zu einer Übereinstimmung kommt, muss die Anzahl, die an "Enumerable.Range" übergeben wurde, nur mehr sein als die maximale Anzahl von Tagen bis zum nächsten Arbeitstag. Meine Schätzung ist 5 oder 6, aber es größer zu machen hat keinen negativen Effekt. – juharr

4

Eine feste Liste der Daten ist eine etwas begrenzte Weise Feiertage zum Ausdruck bringen.

Beachten Sie, dass Ihre Liste nur Daten für das aktuelle Jahr enthält. Wenn also heute der 30. Dezember 2015 ist, wäre der nächste Feiertag der 1. Januar 2016 - den Sie nicht in Ihrer Liste finden würden.

Denken Sie auch daran, dass viele Feiertage nicht jedes Jahr auf dasselbe Datum fallen. Oft sind sie an den Wochentag gebunden und manchmal werden sie von religiösen Kalendern oder willkürlich bestimmt.

Ein robusteres System sollte eine Vielzahl von verschiedenen Arten von Ferien behandeln.Hier ist eine mögliche Implementierung:

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; 
    } 
} 

Mit diesen definiert ist, können wir jetzt Feiertage definieren viel robuster, wie zum Beispiel:

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

Und jetzt können wir eine Methode erstellen, die für den nächsten Werktag überprüft , wie Sie verlangen:

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; 
    } 
} 

Diese Methode auf der abstrakten Holiday Klasse gehen könnte, oder es könnte überall wirklich gehen.

Beispiel der Verwendung (mit obiger Definition für holidays):

var weekendDays = new[] { DayOfWeek.Saturday, DayOfWeek.Sunday }; 

DateTime workDay = GetNextNonHolidayWeekDay(new DateTime(2015, 12, 31), holidays, weekendDays); 
// returns 2016-01-04 

Dies ist immer noch nicht ganz vollständige Lösung though. Viele Feiertage haben komplexere Berechnungsregeln. Als eine Übung, die dem Leser überlassen wird, versuchen Sie, eine Klasse zu implementieren, die von Holiday für den zweiten Tag in den USA Erntedankfest stammt. Der erste Tag fällt immer am 4. Donnerstag im November, aber der zweite Tag ist immer "der Freitag nach dem 4. Donnerstag im November" und nicht nur "der vierte Freitag im November" (siehe November 2019 für ein Beispiel wo dies liegt) Angelegenheiten).

+0

Die Art, wie Sie die Ferien modelliert haben, scheint sehr passend zu sein. Das ist eine großartige Antwort. –

1

Basierend auf der Antwort von @fjardon, können Sie Nager.Date verwenden es enthält Wochenende Logik verschiedener Länder und enthält Feiertage für mehr als 70 Länder.

nuget

PM> install-package Nager.Date 

Code-Snippet

//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)); 
Verwandte Themen