8
\$\begingroup\$

A friend of mine told me he had the following exercises at his interview :

You need to determine when a task will be completed if you are given :

  1. A date when the task is started
  2. The duration of the task in hours (how much hours it should take in total)
  3. The starting time and end time of a working day

Weekends are not work days and you can assume that there are no holidays. Taking this in consideration what's the date when the task will be completed (hours and minutes are taken in consideration) ?

(The original exercise was meant to work with database but I simplified it a bit.)

Here's my solution to the problem :

public class Program
{
    private static void Main()
    {
        DateTime startOfTask = new DateTime(2016, 2, 27, 10, 30, 0);

        TimeSpan durationOftask = new TimeSpan(20, 30, 0);

        TimeSpan startOfDay = new TimeSpan(8, 30, 0);
        TimeSpan endOfDay = new TimeSpan(16, 0, 0);

        DateTime endOfTaskTime = GetEndOfTaskTime(durationOftask, startOfTask, startOfDay, endOfDay);
        Console.WriteLine(endOfTaskTime.ToString("yyyy.MM.dd HH:mm"));
        Console.ReadKey();
    }

    private static DateTime GetEndOfTaskTime(TimeSpan durationOftask, DateTime startOfTask, TimeSpan startOfDay,
        TimeSpan endOfDay)
    {
        DateTime endOfTask = startOfTask;
        TimeSpan timeWorkingOnTask = new TimeSpan(0, 0, 0);
        while (timeWorkingOnTask < durationOftask)
        {
            if (endOfTask.DayOfWeek != DayOfWeek.Saturday && endOfTask.DayOfWeek != DayOfWeek.Sunday)
            {
                TimeSpan timeStartOfToday = new TimeSpan(endOfTask.Hour, endOfTask.Minute, 0);
                TimeSpan workTime = timeStartOfToday == startOfDay
                    ? endOfDay - startOfDay
                    : endOfDay - timeStartOfToday;
                if (timeWorkingOnTask + workTime < durationOftask)
                {
                    endOfTask = endOfTask.Add(workTime);
                    timeWorkingOnTask += workTime;
                }
                else
                {
                    endOfTask = endOfTask.Add(durationOftask - timeWorkingOnTask);
                    break;
                }
            }
            endOfTask = endOfTask.AddDays(1);
            endOfTask = new DateTime(endOfTask.Year, endOfTask.Month, endOfTask.Day, startOfDay.Hours,
                startOfDay.Minutes, 0);
        }
        return endOfTask;
    }
}
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

I would add some checks to make sure the date is correct. For instance your start date is on a Saturday.

-- Updated from comment that task doesn't need to start during working times.

if (startTime.TotalDays > 1)
{
    throw new ArgumentOutOfRangeException("startTime", "Start time greater than 24 hours");
}
if (endTime.TotalDays > 1)
{
    throw new ArgumentOutOfRangeException("endTime", "End time greater than 24 hours");
}
if (endTime < startTime)
{
    throw new ArgumentOutOfRangeException("endTime", "End time before start time");
}

For the logic you don't need to do a loop.

var numberOfDaystoStart = 0;
if (startOfTask.DayOfWeek == DayOfWeek.Saturday)
{
    numberOfDaystoStart = 2;
}
else if (startOfTask.DayOfWeek == DayOfWeek.Sunday)
{
    numberOfDaystoStart = 1;
}
else if (startOfTask.TimeOfDay > endTime)
{
    // Start time after the end of the work day need to move to the next day
    numberOfDaystoStart = 1;
}

var startOffset = TimeSpan.Zero;
if (numberOfDaystoStart > 0 || startOfTask.TimeOfDay < startTime)
{
    // IF started on weekend or after work hours adjust for first work day and start of working hours
    var startOfTaskDate = startOfTask.AddDays(numberOfDaystoStart);
    startOffset =
        new DateTime(startOfTaskDate.Year, startOfTaskDate.Month, startOfTaskDate.Day,
            startTime.Hours, startTime.Minutes, startTime.Seconds, startTime.Milliseconds) - startOfTask;
}


var workingtime = endTime.Subtract(startTime);
var nonWorkingTime = TimeSpan.FromDays(1) - workingtime;

// This will give long from C# 
var numbersOfDays = durationOftask.Ticks/workingtime.Ticks;

// add in the duration then add in the extra time for non work hours
// add in the weekends
var endOfTask = startOfTask
    .Add(durationOftask)
    .Add(startOffset)
    .Add(new TimeSpan(numbersOfDays*nonWorkingTime.Ticks))
    // Add in the extra non-working time for each day
    .AddDays((numbersOfDays/5)*2); // Add in 2 extra days for every 5 days of work

// Need to account for starting later in the week and rolling over weekend
var extraDays = numbersOfDays%5;

var endOffset = TimeSpan.Zero;
if ((int) startOfTask.Add(startOffset).DayOfWeek + extraDays > 5)
{
    endOffset = TimeSpan.FromDays(2);
}
// If we ended after the end of the day need to account for non-worktime
if (endOfTask.TimeOfDay > endTime)
{
    endOffset = endOffset.Add(nonWorkingTime);
}

return endOfTask.Add(endOffset);

If starting before the start time or starting on the weekend if valid then there would need to be special code just for those cases but it would be at the top to just to add to the duration the extra days to move on to the start date.

\$\endgroup\$
2
  • \$\begingroup\$ Can you add those few extra lines for that case where you are starting before the start time or starting on the weekend please ? \$\endgroup\$
    – Denis
    Dec 19, 2016 at 23:20
  • \$\begingroup\$ @denis I've updated the code to account for starting the task not during work hours. Also fixed but of if ended after work hours didn't flip to next day. \$\endgroup\$ Dec 20, 2016 at 15:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.