3

I need to count the total days worked given struct saying which days of the week are worked and a from and to date.

My current algorithm is this:

protected int TotalWorkDays(DateTime From, DateTime dtTo,WorkedDays days)
    {
        int Days = 0;
        DayOfWeek DOW;
        for (DateTime curDate = From; curDate.Date <= dtTo.Date; curDate = curDate.AddDays(1))
        {
            DOW = curDate.DayOfWeek;
            if ((DOW == DayOfWeek.Sunday &  days.Sunday) |
                (DOW == DayOfWeek.Monday &  days.Monday) |
                (DOW == DayOfWeek.Tuesday &  days.Tuesday) |
                (DOW == DayOfWeek.Wednesday &  days.Wednesday) |
                (DOW == DayOfWeek.Thursday &  days.Thursday) |
                (DOW == DayOfWeek.Friday &  days.Friday) |
                (DOW == DayOfWeek.Saturday &  days.Saturday)
               )
            {
                Days += 1;
            }
        }
        return Days;
    }

I'm almost positive this can be done without a loop, but I can't seem to figure out. Can someone help me find a more efficient algorithm?

Earlz
  • 62,085
  • 98
  • 303
  • 499
  • possible duplicate of [.NET Date Compare: Count the amount of working days since a date?](http://stackoverflow.com/questions/165887/net-date-compare-count-the-amount-of-working-days-since-a-date) – Jeff Foster Jun 16 '11 at 15:51
  • 2
    @Jeff - Not a duplicate. The working days here are configurable. – Oded Jun 16 '11 at 15:52
  • http://stackoverflow.com/questions/1617049/calculate-the-number-of-business-days-between-two-dates – Dmytro Laptin Jun 16 '11 at 15:58

6 Answers6

4

Find the number of weeks between the From and To dates (using subtraction and division). Then multiply that by the number of days worked per week. Do some subtraction for the end cases (From/To dates are in the middle of a week).

Coeffect
  • 8,772
  • 2
  • 27
  • 42
1

hmmmm....

Create a dictionary going from DayOfWeek (int if i remember correctly), to bool then....

var DaysWorked = (from dayoffset in Enumerable.Range(0, (To - From).TotalDays)
                  where WorkingDays[From.AddDays(dayoffset).DayOfWeek]
                  select dayoffset).Count();

Not exactly efficient though!

George Duckett
  • 31,770
  • 9
  • 95
  • 162
1

Have a look at this codeproject article which explains how to do it without looping ;)

EDIT: Here's the formula it uses:

  1. Calculate the number of time span in terms of weeks. Call it, W.
  2. Deduct the first week from the number of weeks. W= W-1
  3. Multiply the number of weeks with the number of working days per week. Call it, D.
  4. Find out the holidays during the specified time span. Call it, H.
  5. Calculate the days in the first week. Call it, SD.
  6. Calculate the days in the last week. Call it, ED.
  7. Sum up all the days. BD = D + SD + ED � H.
Iain Ward
  • 9,850
  • 5
  • 34
  • 41
  • But if you read how the algorithm works, you won't need to loop. All you'll need to do is calculate the days worked in the start and end weeks if they are partial and the rest you can work out as you know how many days of each week you've worked – Iain Ward Jun 16 '11 at 15:58
  • Basically what @Mannimarco says, but the formula on the codeproject site would be a good start for what you're trying to do – Iain Ward Jun 16 '11 at 16:01
0

You can take advantage of the fact that days of the week repeat every seven days. Here is a basic outline of an algorithm:

  1. Count the number of days worked in the first partial week.
  2. Count the number of days worked in the last partial week.
  3. Calculate the number of whole weeks in the middle and multiply by number of days worked in a week.
  4. Sum the above three values.
ChrisH
  • 4,788
  • 26
  • 35
0
var workDays = new DayOfWeek[]{ DayOfWeek.Monday, DayOfWeek.Tuesday};
var days = TotalWorkDays(new DateTime(2005,1,12), new DateTime(2005,3,15), workDays);

protected int TotalWorkDays(DateTime start, DateTime end, DayOfWeek[] workDays)
{
    var weeks = (int)Math.Floor((end - start).TotalDays / 7); 
    var days = weeks * workDays.Length;

    //Calc rest
    var d = start.AddDays(weeks * 7);
    while (d <= end)
    {
        if(workDays.Contains(d.DayOfWeek)) 
            days++;
        d = d.AddDays(1);

    }   
    return days;
}
Magnus
  • 45,362
  • 8
  • 80
  • 118
0

You can use the following algorithm:

  • count the working days of the starting week (max 7 iterations)
  • count the weeks between start/end and multiple the weeks with the working days
  • count the working days of the end week (max 7 iterations)

The sample uses the classes Week and DateDiff of the Time Period Library for .NET

// ----------------------------------------------------------------------
public int CountWorkingDays( DateTime start, DateTime end, IList<DayOfWeek> workingDays )
{
  if ( workingDays.Count == 0 )
  {
    return 0;
  }

  Week startWeek = new Week( start );
  Week endWeek = new Week( end );
  int dayCount = 0;

  // start week
  DateTime currentDay = start.Date;
  while ( currentDay < startWeek.End )
  {
    if ( workingDays.Contains( currentDay.DayOfWeek ) )
    {
      dayCount++;
    }
    currentDay = currentDay.AddDays( 1 );
  }

  // between weeks
  DateDiff inBetweenWeekDiff = new DateDiff( startWeek.End, endWeek.Start );
  dayCount += inBetweenWeekDiff.Weeks * workingDays.Count;

  // end week
  currentDay = endWeek.Start.Date;
  while ( currentDay < end )
  {
    if ( workingDays.Contains( currentDay.DayOfWeek ) )
    {
      dayCount++;
    }
    currentDay = currentDay.AddDays( 1 );
  }

  return dayCount;
} // CountWorkingDays

Usage:

// ----------------------------------------------------------------------
public void CountWorkingDaysSample()
{
  DayOfWeek[] workingDays = new [] { DayOfWeek.Monday, DayOfWeek.Tuesday };
  DateTime start = new DateTime( 2011, 3, 1 );
  DateTime end = new DateTime( 2011, 5, 1 );
  Console.WriteLine( "working days: {0}", CountWorkingDays( start, end, workingDays ) );
  // > working days: 19
} // CountWorkingDaysSample