/* Compile with: gcc workdays.c -lm -o workdays */

	/* Calculate nr of day since year 0 in new date-system (from 1615) */

long calc_daynr(int year,int month,int day)
{
  long delsum;
  int temp;
  //DBUG_ENTER("calc_daynr");

  if (year == 0 && month == 0 && day == 0)
    return (0);				/* Skip errors */

  delsum= (long) (365L * year+ 31*(month-1) +day);
  if (month <= 2)
      year--;
  else
    delsum-= (long) (month*4+23)/10;
  temp=(int) ((year/100+1)*3)/4;

  return (delsum+(int) year/4-temp);
} /* calc_daynr */

	/* Calc weekday from daynr */
	/* Returns 0 for monday, 1 for tuesday .... */

int calc_weekday(long daynr,int sunday_first_day_of_week)
{
  //DBUG_ENTER("calc_weekday");
  return ((int) ((daynr + 5L + (sunday_first_day_of_week ? 1L : 0L)) % 7));
}

   /* Calc workdays like excel */
   /* 
      nr of weekends = nr of days / 7
      nr of workdays = nr of days - nr of weekends

      The remainder of the division of nr of days / 7
      could completely or partly comprise a weekend.

      Example:
        MTWTFSSMTWTFSSMTWTFSSMTWTFSSMTWTFSSMTWTFSSMTWTFSSMTWTFSSMTWTFSSMTWTFSS
        0123456012345601234560123456012345601234560123456012345601234560123456
                      01234560123456012345 (2 weeks + 6 days)
                      01234560123456 (2 weeks = 14 days )
                      012345         (the remainder of 6 days begins on the same day as the start date)

      The first line in the table below shows that for a remainder of 6 days,
      when the start date is a Monday there will be one extra weekendday.

      Nr of              Extra     Calculation
      remaining Startday weekend   (Nr of remaining days - (5 - Startday))
      days               days

      6 days    day 0     1        6-(5-0) = 1  1
      6 days    day 1     2        6-(5-1) = 2
      6 days    day 2     2        6-(5-2) = 3
      6 days    day 3     2        6-(5-3) = 4
      6 days    day 4     2        6-(5-4) = 5
      6 days    day 5     2        6-(5-5) = 6
      6 days    day 6     1        6-(5-6) = 7  1

      5 days    day 0     0        5-(5-0) = 0
      5 days    day 1     1        5-(5-1) = 1  1
      5 days    day 2     2        5-(5-2) = 2
      5 days    day 3     2        5-(5-3) = 3
      5 days    day 4     2        5-(5-4) = 4
      5 days    day 5     2        5-(5-5) = 5
      5 days    day 6     1        5-(5-6) = 6  1

      4 days    day 0     0        4-(5-0) = -1
      4 days    day 1     0        4-(5-1) = 0
      4 days    day 2     1        4-(5-2) = 1  1
      4 days    day 3     2        4-(5-3) = 2
      4 days    day 4     2        4-(5-4) = 3
      4 days    day 5     2        4-(5-5) = 4
      4 days    day 6     1        4-(5-6) = 5  1

      3 days    day 0     0
      3 days    day 1     0
      3 days    day 2     0
      3 days    day 3     1
      3 days    day 4     2
      3 days    day 5     2
      3 days    day 6     1

      2 days    day 0     0
      2 days    day 1     0
      2 days    day 2     0
      2 days    day 3     0
      2 days    day 4     1
      2 days    day 5     2
      2 days    day 6     1

      1 days    day 0     0
      1 days    day 1     0
      1 days    day 2     0
      1 days    day 3     0
      1 days    day 4     0
      1 days    day 5     1
      1 days    day 6     1

      If startday = 6 (Sunday) add 1 extra weekendday
      If (Nr of remaining days - (5 - day of week of start day))

         < 1  => 0 weekenddays
         = 1  => 1 weekendday
         >1   => 2 weekenddays (a saturday and a sunday)
*/

int calc_workdays(int y1, int m1, int d1, int y2, int m2, int d2)
{
   int days1    = calc_daynr(y1, m1, d1);
   int days2    = calc_daynr(y2, m2, d2);
   int nrofdays = days2 - days1;

   if (nrofdays==0) return 1;  // e.g. monday - monday = 1 day

   int neg_nrofdays = (nrofdays < 0);
   int dayofweek    = calc_weekday((neg_nrofdays ? days2 : days1), 0);

   //printf("day of week: %d\n",   dayofweek);

   nrofdays = abs(nrofdays)+1; // e.g. diff mon-tue = 1, but takes 2 days

   int nrofweekends = floor(nrofdays / 7);
   int mod          = nrofdays % 7;
   int add          = 0;

   // Correction when the remainder (part of a week)
   // fully or partly comprises a weekend.
   if (mod)
   {
      if ((dayofweek)==6) add=1;
      else
      {
         add = mod - (5 - dayofweek);
         if (add<0) add = 0;
         if (add>1) add = 2;
      }
   }

   //printf("add: %d\n",   add);
   //printf("nrofdays: %d\n",   nrofdays);
   int workdays = nrofdays - ((nrofweekends*2) + add);

   return (neg_nrofdays ? -workdays : workdays);
}

int main (void)
{
   // Print nr of workdays between May 1th, 2007 and Apr 16th, 2007
   //printf("Nr of workdays: %d\n",   calc_workdays(2007, 5, 1, 2007, 4, 16));

   // Print nr of workdays between May 7th, 2007 and May 20th, 2007
   printf("Nr of workdays: %d\n",   calc_workdays(2007, 5, 7, 2007, 5, 20));
}

