Response time with JavaScript Date - Part 3

This is the last part of the tutorial in which we solve the maintenance response time problem. Finally we got to the point when we can create the main function which will return the response date by when the air conditioning company should fix the issue.

If you haven’t read Part 1 and Part 2 of this tutorial, please click on the links and have a look.

Working on getResponseDate

We finally arrived at the point when we can calculate the result. Let’s quickly discuss what we need to do next.

We have to set up a counter, which records the time left from the total response time. Time will start at the report date, so these two quantities will initially be equal.

We can now focus on the core of the logic.

As long as the response time is greater than the time left to the end of the day, the repair can’t happen on that day, so we need to move on to the following day and, at the same time, the response time has to be decreased by the time left to the end of the day (which can even be the whole working day, i.e. 8 hours).

When we get to the point where the response time is less than the remaining time from a given day, we need to add that time to 8am and we are done. See the example at the beginning of the last post for a more detailed explanation.

Let’s then write the code:

function getResponseDate(reportDate, responseTimeInHours = 8) {
  let responseTimeLeftInMs = responseTimeInHours * ONE_HOUR;

  // counter starts at report date
  let responseDate = reportDate;

  while (responseTimeLeftInMs > timeToEndOfDay(responseDate)) {
    responseTimeLeftInMs -= timeToEndOfDay(responseDate);
    responseDate = getNextWorkingDay(responseDate);
  }

  return new Date(responseDate.getTime() + responseTimeLeftInMs);
}

Our counter is called responseDate and it’s initially equal to reportDate.

I created a simple while loop to calculate the result and basically translated the thinking process into code. While the response time is greater than the time left to the end of the day, we subtract that time from the response time and we move on to the following day.

When the response time is less than the time left to the end of the working day, the code inside the while loop won’t run, and we simply add the response time left to the start of the working day, but we already did this in Part 1.

Let’s run the function and we will get the expected result (11:15AM, 12 March 2018). Also, if you follow along with the tests, run them again and both of them will pass now.

Good!

Report time beyond working hours

Unfortunately we have to handle one more special case: The air conditioning breaks in non-working hours and the owner immediately reports the issue. In this case, response time starts ticking at 8am on the following working day since the company can’t do anything in early morning or at late night. If the issue was reported before work, the countdown starts at 8am on the same day, if the report time falls sometime in the evening, it will start on the following day.

Test

Let’s create a test case that covers this situation:

it('gives correct result if report time is outside working hours', () => {
    const reportTime = new Date(2018, 2, 8, 7, 30);
    const responseTime = 5;
    const expected = new Date(2018, 2, 8, 13, 0);

    const result = getResponseDate(reportTime, responseTime);
    expect(result).to.be.eql(expected);
  });

Report time is 7:30am and the response time is 5 hours, so we expect the issue to be fixed by 1pm on the same day as the response time starts at 8am.

Adding the missing piece

We report the issue at 7:30am and the response time is 5 hours. This means that the fix is due by 1pm on the very same day (unless it’s a weekend, of course).

We need handle two scenarios: Report time is before work (like in this example) or after work.

The algorithm has a bug in these cases, namely that the time left to the end of the day will be negative, which doesn’t make much sense, of course, and so we will write a conditional to handle these cases:

// handle outside working hours report time
  if (timeToEndOfDay(responseDate) < 0) {
    responseDate = getNextWorkingDay(responseDate);
  } else if (responseDate < startOfWorkingDay(responseDate)) {
    responseDate = startOfWorkingDay(responseDate);
  }

If the time left to the end of the day is negative (that is the issue was reported after work), we will need to jump to the next working day. If the report time is just before the start of the day, we start counting the response date at that exact time.

If you run the function, you will get the correct results. Same with the tests, all three should pass now.

Enter 17:30 for the report time instead, and you will get 1:00pm on the following day.

Finally, here is the full code:

const ONE_HOUR = 60 * 60 * 1000;
const SATURDAY = 6;
const SUNDAY = 0;
const START_OF_WORK = 8; // o'clock
const END_OF_WORK = 16; // o'clock

const isWeekend = date => date.getDay() === SATURDAY || date.getDay() === SUNDAY;
const startOfWorkingDay = date => new Date(new Date(date).setHours(START_OF_WORK, 0, 0, 0));
const endOfWorkingDay = date => new Date(new Date(date).setHours(END_OF_WORK, 0, 0, 0));

const addOneDay = date => new Date(date.getTime() + ONE_HOUR * 24);

// returns 8am of next working day in ms
const getNextWorkingDay = date => {
  let day;
  for (day = addOneDay(date); isWeekend(day); day = addOneDay(day));
  return startOfWorkingDay(day);
};

const timeToEndOfDay = date => endOfWorkingDay(date) - date; // in ms

function getResponseDate(reportDate, responseTimeInHours = 8) {
  let responseTimeLeftInMs = responseTimeInHours * ONE_HOUR;

  // counter starts at report date
  let responseDate = reportDate;

  // handle outside working hours report time
  if (timeToEndOfDay(responseDate) < 0) {
    responseDate = getNextWorkingDay(responseDate);
  } else if (responseDate < startOfWorkingDay(responseDate)) {
    responseDate = startOfWorkingDay(responseDate);
  }

  while (responseTimeLeftInMs > timeToEndOfDay(responseDate)) {
    responseTimeLeftInMs -= timeToEndOfDay(responseDate);
    responseDate = getNextWorkingDay(responseDate);
  }

  return new Date(responseDate.getTime() + responseTimeLeftInMs);
}

Conclusion

Calculating the response time is among the more complex problems in my opinion. Again, one has to think it over and it probably takes a few tries (depending on experience) to get to a fully working solution.

Personally, I think that this was a good exercise and hope you share my opinion. Enjoy coding!