0
votes

Goal: I'm trying to do date calculations by adding 1 to 3 days depending on what day the user edits the cell. The following are the main conditions that wish to have.

  1. If it's between 2:00 PM and 6:30 PM (JST) on a Monday through Thursday, then add 1 day to the current date.
  2. If it's between 2:00 PM and 6:30 PM (JST) on a Friday, then add 3 days to the current date to get it on a Monday and avoid getting weekend dates.
  3. If Monday or any other week day is not a working day based on Japan's bank holiday system, I want to add as many days necessary until we can get the next working day. For example, the 11th of January, 2021 is a Monday, however for Japan, it's a bank holiday, so in this scenario, I would like to get the 12th of January.

Issue I don't know how or if it's even possible to somehow automatically add dates and always get a working day that's based on Japan's bank holiday system.

Current Code This is the code that I have at the moment, which I kindly received from @NaziA and it works great as it exclude weekends. But I'm just wondering if we can take it a step further by also excluding Japan's bank holidays and only get Japan's working days. Continuation to this question.

function onEdit(e) { 
  const sheetName = 'Status';
  const statusSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
  const cell = statusSheet.getActiveCell();
  const row = cell.getRow();
  const column = cell.getColumn();
  const editedRangeValue = cell.getValue();

  // var currentDate = new Date(new Date().toLocaleString('en-US', { timeZone: 'Japan' }));

  // The above line of code translates your time into japan timezone but since 
  // sheets is converting your timezone automatically to the regional one, 
  // it adjusts an additional day when used. Be careful in using the above line.

  var currentDate = new Date();

  // Use the line below to test different time slots, we do not want to wait the 
  // exact time just to test this script.
  // currentDate.setHours(11, 30, 0 ,0);

  startDate = new Date(currentDate.getTime());
  startDate.setHours(9, 30, 0, 0);

  endDate1 = new Date(currentDate.getTime());
  endDate1.setHours(14, 0, 0, 0);

  endDate2 = new Date(currentDate.getTime());
  endDate2.setHours(18, 30, 0, 0);

  // Column numbers for Status sheet
  const caseTypeColumnStatus = 4;

  if (sheetName == 'Status' && column == caseTypeColumnStatus && row > 15 && editedRangeValue == 'Chat => Email') {
    var statusColumn = column + 3;
    if (startDate <= currentDate && endDate1 > currentDate) { // If the current time is between 9:30 A.M. and 2:00 P.M. (JST), then TRUE. 
      // Insert today's date
      statusSheet.getRange(row, statusColumn).setValue(currentDate);
    } else if (endDate1 <= currentDate && endDate2 > currentDate) { // If the current time is between 2:00 P.M. and 6:30 P.M. (JST), then TRUE.
      // Insert tomorrow's date
      var tomorrow = new Date(currentDate.setDate(currentDate.getDate() + 1));
      if(tomorrow.getDay() == 6){ 
        // if saturday, add 2 days 
        tomorrow.setDate(tomorrow.getDate() + 2);
      } 
      else if(tomorrow.getDay() == 0){
        // if sunday, add 1 day
        tomorrow.setDate(tomorrow.getDate() + 1);
      } 
      statusSheet.getRange(row, statusColumn).setValue(tomorrow);
    }
  }
}
1

1 Answers

1
votes

Sorry if it took some time. I have created a separate function for calculating offset for weekend and holiday.

My test cases are written as comment below, kindly comment them out so you can see the behavior.

These test cases are the extreme ones, so when they give the expected result, you should be good on other dates as well. (E.g. consecutive/continuous holidays, holiday + weekends and next year holiday with weekend)

function onEdit(e) { 
  const sheetName = 'Status';
  const statusSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
  const cell = statusSheet.getActiveCell();
  const row = cell.getRow();
  const column = cell.getColumn();
  const editedRangeValue = cell.getValue();

  var currentDate = new Date(new Date().toLocaleString('en-US', { timeZone: 'Japan' }));
  // var currentDate = new Date();
  // Testing next day of April 28, 2021 = > should be May 6, 2021
  // currentDate.setFullYear(2021);
  // currentDate.setMonth(3);
  // currentDate.setDate(28);
  // currentDate.setHours(15, 30, 0 ,0);
  // Testing next day of December 31, 2020 => should be January 4, 2021
  // currentDate.setFullYear(2020);
  // currentDate.setMonth(11);
  // currentDate.setDate(31);
  // currentDate.setHours(15, 30, 0 ,0);
  // Check date if value assigned is correct
  // Logger.log(currentDate);

  startDate = new Date(currentDate.getTime());
  startDate.setHours(9, 30, 0, 0);

  endDate1 = new Date(currentDate.getTime());
  endDate1.setHours(14, 0, 0, 0);

  endDate2 = new Date(currentDate.getTime());
  endDate2.setHours(18, 30, 0, 0);

  // Column numbers for Status sheet
  const caseTypeColumnStatus = 4;

  if (sheetName == 'Status' && column == caseTypeColumnStatus && row > 15 && editedRangeValue == 'Chat => Email') {
    var statusColumn = column + 3;
    if (startDate <= currentDate && endDate1 > currentDate) { // If the current time is between 9:30 A.M. and 2:00 P.M. (JST), then TRUE. 
      // Insert today's date with the hour set to 00:00:00
      // Calling holidayWeekendOffset for same day just in case 
      // a user edits the sheet data during weekends/holidays. 
      // It will just adjust when it's not a weekday
      currentDate = new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate());
      currentDate = holidayWeekendOffset(currentDate);
      statusSheet.getRange(row, statusColumn).setValue(currentDate);
      // Check output
      // Logger.log(currentDate);
    } else if (endDate1 <= currentDate && endDate2 > currentDate) { // If the current time is between 2:00 P.M. and 6:30 P.M. (JST), then TRUE.
      // Insert tomorrow's date with the hour set to 00:00:00
      currentDate = new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate());
      var tomorrow = new Date(currentDate.setDate(currentDate.getDate() + 1));
      // Adjust tomorrow's date based on holidays and weekends
      tomorrow = holidayWeekendOffset(tomorrow);
      statusSheet.getRange(row, statusColumn).setValue(tomorrow);
      // Check output
      // Logger.log(tomorrow);
    }
  }
}

function holidayWeekendOffset(day) {
  var today = day;
  // define holidays, should be in ascending order so successive holidays can be caught properly
  var holidays = [{month: 1.0, date: 1.0},
                  {month: 1.0, date: 11.0},
                  {month: 2.0, date: 11.0},
                  {month: 2.0, date: 23.0},
                  {month: 3.0, date: 20.0},
                  {month: 4.0, date: 29.0},
                  {month: 4.0, date: 30.0},
                  {month: 5.0, date: 1.0},
                  {month: 5.0, date: 2.0},
                  {month: 5.0, date: 3.0},
                  {month: 5.0, date: 4.0},
                  {month: 5.0, date: 5.0},
                  {month: 7.0, date: 22.0},
                  {month: 7.0, date: 23.0},
                  {month: 8.0, date: 8.0},
                  {month: 9.0, date: 20.0},
                  {month: 9.0, date: 23.0},
                  {month: 11.0, date: 3.0},
                  {month: 11.0, date: 23.0}]

  holidays.forEach(function (holiday) {
    if (today.getDate() == holiday.date && today.getMonth() + 1 == holiday.month){
      today.setDate(today.getDate() + 1);
    }
  });
  while (today.getDay() == 6 || today.getDay() == 0) {
    today.setDate(today.getDate() + 1);
  }
  return today;
}

There are libraries available like this but I avoided to use it since I am not sure if it would be updated every now and then. At least with this, you have control over the function and can define it any way you want.