tags:

views:

641

answers:

8

I need to find 2 elegant complete implementations of

public static DateTime AddBusinessDays(this DateTime date, int days)
{
 // code here
}

and 

public static int GetBusinessDays(this DateTime start, DateTime end)
{
 // code here
}

O(1) preferable (no loops).

EDIT: By business days i mean working days (Monday, Tuesday, Wednesday, Thursday, Friday). No holidays, just weekends excluded.

I already have some ugly solutions that seem to work but i wonder if there are elegant ways to do this. Thanks

A: 

The only real solution is to have those calls access a database table that defines the calendar for your business. You could code it for a Monday to Friday workweek without too much difficult but handling holidays would be a challenge.

Edited to add non-elegant and non-tested partial solution:

public static DateTime AddBusinessDays(this DateTime date, int days)
{
    for (int index = 0; index <= days; index++;)
    {
        switch (date.DayOfWeek)
        {
            case DayOfWeek.Friday:
                date.AddDays(3);
                break;
            case DayOfWeek.Saturday:
                date.AddDays(2);
                break;
            default:
                date.AddDays(1);
                break;
         }
    }
    return date;
}

Also I violated the no loops requirement.

Jamie Ide
don't care about holidays
AZ
I don't think the Saturday case would ever be hit.
Dennis Palmer
@Dennis -- it would if the date passed in is a Saturday.
Jamie Ide
A: 

With internationalization, this is difficult. As mentioned in other threads here on SOF, holidays differ from country to country certainly and even from province to province. Most governments do not schedule out their holidays more than five years or do.

Ash Machine
I can't understand why someone downvoted Ash: it's true that it's not an answer to what was asked, but Ash IS TOTALLY RIGHT, I have the very same issue: my software has to run in Europe (Mon-Sun) AND Africa/Asia (Sat-Fri). To downvote this, one has to be an ass (the animal, just in case) who can't see beyond his nose: it looks like there are people on SO who are afraid of knowledge, just don't want to know and prefer to remain on their reassuring ignorance: but they don't do a service to AZ in first place (and to themself too, but it's their own business)
Turro
i (the OP) downvoted because i thought i made it clear in the question what are the constraints and it seemed Ash dind not pay attention. I apologize if that hurt someones feeling but is just a downvote
AZ
+13  A: 

Latest attempt for your first function:

public static DateTime AddBusinessDays(DateTime date, int days)
{
    if (days == 0) return date;

    if (date.DayOfWeek == DayOfWeek.Saturday)
    {
        date = date.AddDays(2);
        days -= 1;
    }
    else if (date.DayOfWeek == DayOfWeek.Sunday)
    {
        date = date.AddDays(1);
        days -= 1;
    }

    date = date.AddDays(days / 5 * 7);
    int extraDays = days % 5;

    if ((int)date.DayOfWeek + extraDays > 5)
    {
        extraDays += 2;
    }

    return date.AddDays(extraDays);

}

The second function, GetBusinessDays, can be implemented as follows:

public static int GetBusinessDays(DateTime start, DateTime end)
{
    if (start.DayOfWeek == DayOfWeek.Saturday)
    {
        start = start.AddDays(2);
    }
    else if (start.DayOfWeek == DayOfWeek.Sunday)
    {
        start = start.AddDays(1);
    }

    if (end.DayOfWeek == DayOfWeek.Saturday)
    {
        end = end.AddDays(-1);
    }
    else if (end.DayOfWeek == DayOfWeek.Sunday)
    {
        end = end.AddDays(-2);
    }

    int diff = (int)end.Subtract(start).TotalDays;

    int result = diff / 7 * 5 + diff % 7;

    if (end.DayOfWeek < start.DayOfWeek)
    {
        return result - 2;
    }
    else{
        return result;
    }
}
Patrick McDonald
For the second one, one solution is to take the difference between date and date+days. This is nice in that it guarantees that the two functions will sync properly, and it removes redundancy.
Brian
@Patrick: Should be fixed now - hope you don't mind the edit. (Just rollback if I've done something silly.)
Noldorin
Feed current date, run for 0 to 10 business days, it always fail on Wednesday.
Adrian Godong
latest posting seems to work okay
Patrick McDonald
Ah, I really shouldn't be rushing this...
Noldorin
Yeah, we got there in the end. (I say 'we' for my tiny contribution!) Up-voted for the effort.
Noldorin
Thanks for your input Noldorin, I can only upvote your comments unfortunately!
Patrick McDonald
Haha, no worries. With the rep I currently have, it's of little consequence. Now, it would be interesting to see a similar implementation of `GetBusinessDays` (the approach should be similar conceptually) - might have a think later.
Noldorin
This didn't work for me when I converted it to VB.Net -- not sure if it's a bug in my conversion or the main logic, but I ran it on 10/1/09, adding 3 business days and it was giving me back 10/8/09 -- it should be giving me 10/6/09.
Nicholas H
+2  A: 
public static DateTime AddBusinessDays(this DateTime date, int days)
{
    date = date.AddDays((days / 5) * 7);

    int remainder = days % 5;

    switch (date.DayOfWeek)
    {
        case DayOfWeek.Tuesday:
            if (remainder > 3) date = date.AddDays(2);
            break;
        case DayOfWeek.Wednesday:
            if (remainder > 2) date = date.AddDays(2);
            break;
        case DayOfWeek.Thursday:
            if (remainder > 1) date = date.AddDays(2);
            break;
        case DayOfWeek.Friday:
            if (remainder > 0) date = date.AddDays(2);
            break;
        case DayOfWeek.Saturday:
            if (days > 0) date = date.AddDays((remainder == 0) ? 2 : 1);
            break;
        case DayOfWeek.Sunday:
            if (days > 0) date = date.AddDays((remainder == 0) ? 1 : 0);
            break;
        default:  // monday
            break;
    }

    return date.AddDays(remainder);
}
LukeH
A: 

This is what i've written so far. It works in all cases and does negatives too. Still need a GetBusinessDays implementation

public static DateTime AddBusinessDays(this DateTime startDate,
                                         int businessDays)
{
    int direction = Math.Sign(businessDays);
    if(direction == 1)
    {
     if(startDate.DayOfWeek == DayOfWeek.Saturday)
     {
      startDate = startDate.AddDays(2);
      businessDays = businessDays - 1;
     }
     else if(startDate.DayOfWeek == DayOfWeek.Sunday)
     {
      startDate = startDate.AddDays(1);
      businessDays = businessDays - 1;
     }
    }
    else
    {
     if(startDate.DayOfWeek == DayOfWeek.Saturday)
     {
      startDate = startDate.AddDays(-1);
      businessDays = businessDays + 1;
     }
     else if(startDate.DayOfWeek == DayOfWeek.Sunday)
     {
      startDate = startDate.AddDays(-2);
      businessDays = businessDays + 1;
     }
    }

    int initialDayOfWeek = Convert.ToInt32(startDate.DayOfWeek);

    int weeksBase = Math.Abs(businessDays / 5);
    int addDays = Math.Abs(businessDays % 5);

    if((direction == 1 && addDays + initialDayOfWeek > 5) ||
         (direction == -1 && addDays >= initialDayOfWeek))
    {
     addDays += 2;
    }

    int totalDays = (weeksBase * 7) + addDays;
    return startDate.AddDays(totalDays * direction);
}
AZ
+2  A: 

I know I'm late to the game here, but here's yet another take on the AddBusinessDays method. Works with negative values, too. w00t!

public static DateTime AddBusinessDays(this DateTime dateTime, int businessDays)
{
 if (businessDays == 0) return dateTime;

 int weeks = businessDays / 5;
 int totalDays = (weeks * 7) + (businessDays % 5);

 DateTime result = dateTime.AddDays(totalDays);

 bool isNegative = businessDays < 0;
 if (isNegative)
 {
  // If we're subtracting days and the result falls on a day that is later in the
  // week than the day we started on, then we crossed over a weekend and need to
  // subtract two more days.
  if (result.DayOfWeek > dateTime.DayOfWeek)
  {
   result = result.AddDays(-2);
  }
 }

 // Handle the case where the result falls on a weekend.
 if (result.DayOfWeek == DayOfWeek.Saturday)
 {
  if (isNegative)
   result = result.AddDays(-1);
  else
   result = result.AddDays(2);
 }
 else if (result.DayOfWeek == DayOfWeek.Sunday)
 {
  if (isNegative)
   result = result.AddDays(-2);
  else
   result = result.AddDays(1);
 }

 return result;
}
Skinniest Man
+6  A: 

using Fluent DateTime http://fluentdatetime.codeplex.com/

        var now = DateTime.Now;
        var dateTime1 = now.AddBusinessDays(3);
        var dateTime2 = now.SubtractBusinessDays(5);

internal code is as follows

    /// <summary>
    /// Adds the given number of business days to the <see cref="DateTime"/>.
    /// </summary>
    /// <param name="current">The date to be changed.</param>
    /// <param name="days">Number of business days to be added.</param>
    /// <returns>A <see cref="DateTime"/> increased by a given number of business days.</returns>
    public static DateTime AddBusinessDays(this DateTime current, int days)
    {
        var sign = Math.Sign(days);
        var unsignedDays = Math.Abs(days);
        for (var i = 0; i < unsignedDays; i++)
        {
            do
            {
                current = current.AddDays(sign);
            }
            while (current.DayOfWeek == DayOfWeek.Saturday ||
                current.DayOfWeek == DayOfWeek.Sunday);
        }
        return current;
    }

    /// <summary>
    /// Subtracts the given number of business days to the <see cref="DateTime"/>.
    /// </summary>
    /// <param name="current">The date to be changed.</param>
    /// <param name="days">Number of business days to be subtracted.</param>
    /// <returns>A <see cref="DateTime"/> increased by a given number of business days.</returns>
    public static DateTime SubtractBusinessDays(this DateTime current, int days)
    {
        return AddBusinessDays(current, -days);
    }
Simon
This is the only solution that actually worked for me when converted to VB.Net
Nicholas H
Nice and elegant.
abraginsky
A: 

Here is solution of ultimate elegance:

http://alecpojidaev.wordpress.com/2009/10/29/work-days-calculation-with-c/

Alec Pojidaev