简体   繁体   中英

Find next 5 working days starting from today

I use nager.date to know if a day is a holiday day or a weekend day Saturday and Sunday).
I need to extract the date (starting from today or any other date) after 5 working days.

DateTime date1 = new DateTime(2019, 12, 23); 
int i = 0;
    
while ( i < 5)
{
   if (DateSystem.IsPublicHoliday(date1, CountryCode.IT) || DateSystem.IsWeekend(date1, CountryCode.IT))
   {
       date1 = date1.AddDays(1); 
   }
   else 
   {
       date1= date1.AddDays(1);
       i++;
   }
}

The problem of this code is that if the last else occurs, it add me 1 day but without doing any other check.

For example:
If the start date is 13/07/2020, the result will be at the end 18/07/2020 and as you can see is on Saturday.

How could I modify this code to achieve what I need?

The order is important. The AddDays should be called first, and after it is called we check if the new day matches our criteria.

Note : I have renamed the i variable so it is more clear.

DateTime date1 = new DateTime(2019, 12, 23); 
int daysAdded = 0;

while (daysAdded < 5)
{
    date1 = date1.AddDays(1);
    if (!DateSystem.IsPublicHoliday(date1, CountryCode.IT) && !DateSystem.IsWeekend(date1, CountryCode.IT)) {
        // We only consider laboral days
        // laboral days: They are not holidays and are not weekends
        daysAdded ++;
    }
}

I always try to generalize my solutions, so here's one enabling LINQ:

public bool IsWorkingDay(DateTime dt)
    => !DateSystem.IsPublicHoliday(dt) && !DateSystem.IsWeekend(dt);

public DateTime NextWorkingDay(DateTime dt)
{
    dt = dt.AddDays(1);
    while (!IsWorkingDay(dt))
        dt = dt.AddDays(1);
    return dt;
}

public IEnumerable<DateTime> WorkingDaysFrom(DateTime dt)
{
    if (!IsWorkingDay(dt))
        dt = NextWorkingDay(dt); // includes initial dt, remove if unwanted
    while (true)
    {
        yield return dt;
        dt = NextWorkingDay(dt);
    }
}

This will pump out working days from a given date until end of time, and then use LINQ to grab the number you want:

var next5 = WorkingDaysFrom(DateTime.Today).Take(5).ToList();

here's how to get all the working days in 2020:

var working2020 = WorkingDaysFrom(new DateTime(2020, 1, 1))
    .TakeWhile(dt => dt.Year == 2020)
    .ToList();
DateTime date1 = new DateTime(2019, 12, 23); 
int i = 0;
while ( i < 5)
{
   date1 = date1.AddDays(1); 
   if (!DateSystem.IsPublicHoliday(date1, CountryCode.IT) && !DateSystem.IsWeekend(date1, CountryCode.IT)) 
    {           
        i++;
    }

}

but I think that you need a DateTime[] to store all the five days

This is a better and a faster way to do this without using third party libraries.

DateTime nowDate = DateTime.Now;

DateTime expectedDate;
if (nowDate.DayOfWeek == DayOfWeek.Saturday)
{
    expectedDate = nowDate.AddDays(6);
}
else if (nowDate.DayOfWeek == DayOfWeek.Sunday)
{
    expectedDate = nowDate.AddDays(5);
}
else
{
    expectedDate = nowDate.AddDays(7);
}

I thought about the problem, and based on the LINQ suggestion Lasse-v-Karlsen made, developed this code, which gives you most flexibility:

void Main()
{       
    // a list of public holidays
    var holidays = new List<DateTime>() {new DateTime(2020,1,1), 
                   new DateTime(2020,12,24), new DateTime(2020,12,25), new DateTime(2020,12,26)};
    // a function checking if the date is a public holiday
    Func<DateTime, bool> isHoliday = (dt) => holidays.Any(a=>a==dt);
    
    // the start date
    var dt = new DateTime(2020, 07, 13);
    // end date, 5 working days later
    var endDate = GetWorkingDay(dt, 5, isHoliday);
    // print it
    Console.WriteLine(endDate?.ToString("yyyy-mm-dd"));
}

public DateTime? GetWorkingDay(DateTime dt, int skipWorkingDays = 0, 
                                Func<DateTime, bool> holidays=null)
{
    if (holidays == null) holidays = (dt) => false;
    IEnumerable<DateTime> NextWorkingDay(DateTime dt)
    {
        while (true)
        {
            var day = dt.DayOfWeek;
            if (day != DayOfWeek.Saturday && day != DayOfWeek.Sunday 
                && !holidays.Invoke(dt)) yield return dt;
            dt = dt.AddDays(1);
        }
    }

    if (skipWorkingDays<0) return null;
    if (skipWorkingDays==0) return NextWorkingDay(dt).First();
    var nextXDays = NextWorkingDay(dt).Take(skipWorkingDays).ToList();
    var endDate = nextXDays.OrderByDescending(d => d).First();
    return endDate;
}

Whether you have a list of public holidays like in this example, or a function coming from a library telling you if a date is a public holiday or not, just feel free to modify the Lambda function isHoliday . In your case, it would be defined as:

Func<DateTime, bool> isHoliday = (dt) => DateSystem.IsPublicHoliday(dt, CountryCode.IT); 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM