Determine the difference between two DateTimes, only counting opening hours

Here we go, spent a while on this one for you. :)

Has room to detect holidays (if you write a function that checks whether a DateTime is a holiday), detects weekends in between the dates, and handles more than just hours.

The algorithm is to compute how much time is from the start til close of business and from the start of business to the end time, and then compute how many days are in between. Falling on the same date is a special case.

Caveat: I did some basic testing but probably didn't get all corner cases.

    public static TimeSpan BusinessTimeDelta(DateTime start, DateTime stop)
    {
        if (start == stop)
            return TimeSpan.Zero;

        if (start > stop)
        {
            DateTime temp = start;
            start = stop;
            stop = temp;
        }

        // First we are going to truncate these DateTimes so that they are within the business day.

        // How much time from the beginning til the end of the day?
        DateTime startFloor = StartOfBusiness(start);
        DateTime startCeil = CloseOfBusiness(start);
        if (start < startFloor) start = startFloor;
        if (start > startCeil) start = startCeil;

        TimeSpan firstDayTime = startCeil - start;
        bool workday = true; // Saves doublechecking later
        if (!IsWorkday(start))
        {
            workday = false;
            firstDayTime = TimeSpan.Zero;
        }

        // How much time from the start of the last day til the end?
        DateTime stopFloor = StartOfBusiness(stop);
        DateTime stopCeil = CloseOfBusiness(stop);
        if (stop < stopFloor) stop = stopFloor;
        if (stop > stopCeil) stop = stopCeil;

        TimeSpan lastDayTime = stop - stopFloor;
        if (!IsWorkday(stop))
            lastDayTime = TimeSpan.Zero;

        // At this point all dates are snipped to within business hours.

        if (start.Date == stop.Date)
        {
            if (!workday) // Precomputed value from earlier
                return TimeSpan.Zero;

            return stop - start;
        }

        // At this point we know they occur on different dates, so we can use
        // the offset from SOB and COB.

        TimeSpan timeInBetween = TimeSpan.Zero;
        TimeSpan hoursInAWorkday = (startCeil - startFloor);

        // I tried cool math stuff instead of a for-loop, but that leaves no clean way to count holidays.
        for (DateTime itr = startFloor.AddDays(1); itr < stopFloor; itr = itr.AddDays(1))
        {
            if (!IsWorkday(itr))
                continue;

            // Otherwise, it's a workday!
            timeInBetween += hoursInAWorkday;
        }

        return firstDayTime + lastDayTime + timeInBetween;
    }

    public static bool IsWorkday(DateTime date)
    {
        // Weekend
        if (date.DayOfWeek == DayOfWeek.Saturday || date.DayOfWeek == DayOfWeek.Sunday)
            return false;

        // Could add holiday logic here.

        return true;
    }

    public static DateTime StartOfBusiness(DateTime date)
    {
        return new DateTime(date.Year, date.Month, date.Day, 9, 0, 0);
    }

    public static DateTime CloseOfBusiness(DateTime date)
    {
        return new DateTime(date.Year, date.Month, date.Day, 17, 0, 0);
    }

DateTime start = DateTime.Parse("15/02/2011 16:00");
DateTime end = DateTime.Parse("16/02/2011 10:00");

int count = 0;

for (var i = start; i < end; i = i.AddHours(1))
{
    if (i.DayOfWeek != DayOfWeek.Saturday && i.DayOfWeek != DayOfWeek.Sunday)
    {
        if (i.TimeOfDay.Hours >= 9 && i.TimeOfDay.Hours < 17)
        {
            count++;
        }
    }
}

Console.WriteLine(count);

Tags:

C#

Datetime