-3

Given a Date. How can I add a number of days to it while skipping weekends and other holidays coming between the range?

List <DateTime> holidays = new List<DateTime>()
{
     new DateTime(2012, 01, 03),
     new DateTime(2012, 01, 26)
}; 

dateTimeReview.Value = CalculateFutureDate(dateTimeStart.Value, 7,holidays);

static DateTime CalculateFutureDate(DateTime fromDate, int numberofWorkDays, ICollection<DateTime> holidays)
{
     var futureDate = fromDate;

     for (var i = 0; i < numberofWorkDays; i++ )
     {
          if (futureDate.DayOfWeek == DayOfWeek.Saturday 
             || futureDate.DayOfWeek == DayOfWeek.Sunday
             || (holidays != null && holidays.Contains(futureDate)))
          {
              futureDate = futureDate.AddDays(1); // Increase FutureDate by one because of condition
              futureDate = futureDate.AddDays(1); // Add a working day
          }
     }
     return futureDate;
}
Cheng Chen
  • 42,509
  • 16
  • 113
  • 174
akb.zak
  • 21
  • 1
  • 10

4 Answers4

4

To skip holidays you will first need to create your own list of holidays. Holidays are different in every country and also subject to other factors.

Then you should add days one by one in a loop with a check if the added day is not a weekend day and does not occur in the list of holidays, until the given number of days has been added.

Unfortunately, there is no easier way to do this.

Peladao
  • 4,036
  • 1
  • 23
  • 43
3

I tried the code above and didn't work. The returned date will somehow includes the holidays and weekends as well. I also want to check that the returned date to be on Workdays only.

So, below are my modified codes.

Basically it will calculate the number of workdays to be added and if the end date falls on holidays/weekends, shift the date to the next day.

Do take note that this is on an assumption that the start date is not on weekends/holidays.

static DateTime CalculateFutureDate(DateTime fromDate, int numberofWorkDays,   
                                    ICollection<DateTime> holidays)
{
     var futureDate = fromDate;
     for (var i = 0; i < numberofWorkDays; i++ )
     {
          if (futureDate.DayOfWeek == DayOfWeek.Saturday 
             || futureDate.DayOfWeek == DayOfWeek.Sunday
             || (holidays != null && holidays.Contains(futureDate)))
          {
              futureDate = futureDate.AddDays(1);
              numberofWorkDays++;
          }
          else
          {
              futureDate = futureDate.AddDays(1);
          }
     }
     while(futureDate.DayOfWeek == DayOfWeek.Saturday 
             || futureDate.DayOfWeek == DayOfWeek.Sunday
             || (holidays != null && holidays.Contains(futureDate)))
     {
          futureDate = futureDate.AddDays(1);
     }
     return futureDate;
}
ElenaSofea
  • 31
  • 3
  • Hi Elena, thanks for posting a solution. It's a really good idea to give a full explanation of why your code works, whereas that other doesn't - so that people following along at home can learn something. Can you add that? – Taryn East Jun 16 '13 at 23:35
  • 2
    Edited as per requested.. :) – ElenaSofea Jun 17 '13 at 11:45
0
public static DateTime AddBusinessDays(DateTime pActualDate, int pNumberofWorkDays)
        {
            ICollection<DateTime> holidays = GetAllHolidays();
            int i = default(int);
            while (i < pNumberofWorkDays)
            {
                pActualDate = pActualDate.AddDays(1);
                if (pActualDate.DayOfWeek == DayOfWeek.Saturday || pActualDate.DayOfWeek == DayOfWeek.Sunday
                    || (holidays != null && holidays.Contains(pActualDate))) { }
                else
                { i++; }
            }
            return pActualDate;
        }


private static ICollection<DateTime> GetAllHolidays()
        {
            ICollection<DateTime> holidays = GetPublicHolidays().Select(s => s.Holidays).ToList();
            HashSet<DateTime> finalHolidays = new HashSet<DateTime>();

            //if sunday holiday then the following monday will be holiday
            bool isMonday = GetCalendar().Any(s => s.Type == "KR" && s.IsMonday);

            foreach (var hol in holidays)
            {
                if (hol.DayOfWeek == DayOfWeek.Sunday && isMonday)
                {
                    //adding monday following day holiday to the list
                    finalHolidays.Add(hol.AddDays(1));
                }
            }
            //exclude weekends from the holiday list
            var excludeWeekends = holidays.Where(s => s.DayOfWeek == DayOfWeek.Sunday || s.DayOfWeek == DayOfWeek.Saturday);
            //adding monday to the existing holiday collection
            finalHolidays.UnionWith(holidays.Except(excludeWeekends));
            return finalHolidays;
        }
KrishOnline
  • 488
  • 1
  • 5
  • 16
0

I've built something similar to check for Office Hours:

    public static DateTime AddBusinessHours(DateTime date, long hours)
    {
        int i = 0;

        DateTime tmpDate = date;

        do
        {
            tmpDate = tmpDate.AddHours(1);
            if (!IsWeekend(tmpDate) && !IsHoliday(tmpDate) && IsOfficeHours(tmpDate))
                i++;
        }
        while (i < hours);

        return tmpDate;
    }


    public static bool IsWeekend(DateTime date)
    {
        return (date.DayOfWeek == DayOfWeek.Saturday || date.DayOfWeek == DayOfWeek.Sunday);
    }


    public static bool IsHoliday(DateTime date)
    {
        //All dates in the holiday calendar are without hours and minutes.
        //With the normal date object, the Contains does not work.
        DateTime tmp = new DateTime(date.Year, date.Month, date.Day); 

        HolidayCalendar calendar = HolidayCalendar.Instance;

        return (calendar.Dates.Contains(tmp));
    }


    public static bool IsOfficeHours(DateTime date)
    {
        return (date.Hour >= 8 && date.Hour < 20);   //Office Hours are between 8AM and 8PM
    }

But as mentioned above, you need to run your own holiday calendar.

Remy
  • 12,555
  • 14
  • 64
  • 104