Date, Calc after N Business Days
Date, Calc after N Business Days
Calculates N business days after the specified date. If N is negative, returns N business days ago. It is necessary to set a holiday in advance. It is also possible to add company-original holidays.
Configs
  • A1: Set All Holidays in each line (“2021-12-25,Christmas”)#{EL}
  • B1: Set Days of Week to be Closed (eg “0,Sun” )#{EL}
  • B2: Set Special Days that not be closed (“2022-04-01”)#{EL}
  • C1: Set Date/Datetime of Start (eg “2022-04-29” ) *#{EL}
  • C2: Set Number of Days to Add (eg “3” “-2” ) *#{EL}
  • D1: Select DATE DATA that stores Calced Date (update) *
  • D2: Select STRING DATA that stores Non-business Days (update)
Script (click to open)
// GraalJS Script (engine type: 2)

//////// START "main()" /////////////////////////////////////////////////////////////////
main();
function main(){ 

//// == Config Retrieving / 工程コンフィグの参照 ==
const strHolidays     = configs.get( "StrConfA1" );        // NotRequired ///////////
  if( strHolidays   === "" ){
    engine.log( " AutomatedTask ConfigWarning:" +
                " Config {A1: Holidays} is empty" );
  }
  let arr2dHolidays   = parseTwoColOfCsv( strHolidays );
const strClosedDays   = configs.get( "StrConfB1" );        // NotRequired ///////////
  if( strClosedDays === "" ){
    engine.log( " AutomatedTask ConfigWarning:" +
                " Config {B1: Closed DaysOfWeek} is empty" );
  }
  let arr2dClosedDays = parseTwoColOfCsv( strClosedDays );
const strOpenDespiteWeekends = configs.get( "StrConfB2" ); // NotRequired ///////////
  let arrOpenDespiteWeekends = strOpenDespiteWeekends.split("\n");
const strTarget       = configs.get( "StrConfC1" );        /// REQUIRED /////////////
  if( strTarget     === "" ){
    throw new Error( "\n AutomatedTask ConfigError:" +
                     " Config {C1: Date or Datetime} is empty \n" );
  }
  let dateTarget      = parseDateAsMidnight( strTarget ); // the first of the date
const strToAdd        = configs.get( "StrConfC2" );        /// REQUIRED /////////////
  let numToAdd        = parseInt( strToAdd );
  if( isNaN( numToAdd ) ){
    throw new Error( "\n AutomatedTask ConfigError:" +
                     " Config {C2} is not an integer \n" );
  }
const datePocketOutput = configs.getObject( "SelectConfD1" ); /// REQUIRED ///////////
const strPocketNonbiz  = configs.getObject( "SelectConfD2" ); // NotRequired ///////////


//// == Data Retrieving / ワークフローデータの参照 ==
// (Nothing. Retrieved via Expression Language in Config Retrieving)


//// == Calculating / 演算 ==
if( numToAdd === 0 ){
  engine.log( " AutomatedTask ConfigWarning:" +
              " Config {C2} is Zero, Not calculated" );
  engine.setData( datePocketOutput, new java.sql.Date( dateTarget.getTime() ) );
  return;
}


Date.prototype.toBpmsYMD = function() {
  var dd = this.getDate(); var mm = this.getMonth() + 1; // getMonth() is zero-based
  return [ this.getFullYear(), (mm>9 ? '' : '0') + mm, (dd>9 ? '' : '0') + dd ].join('-');
};
let n = 0;
let strNonbiz = "";

while( n !== numToAdd ){
  if( numToAdd < 0 ){ dateTarget.setDate( dateTarget.getDate() - 1 ); }
  if( numToAdd > 0 ){ dateTarget.setDate( dateTarget.getDate() + 1 ); }
  engine.log( " AutomatedTask RuntimeLog:" +
              " DayOfWeek of " + dateTarget.toBpmsYMD() + " (Sun=0,Mon=1): " + dateTarget.getDay() );

  let strOutput = "";
  for( let i = 0; i < arr2dHolidays.length; i++ ){
    let dateTmp = parseDateAsMidnight( arr2dHolidays[i][0] ); // the first of the date, "00:00"
    if( dateTmp.getTime() === dateTarget.getTime() ){
      strOutput = arr2dHolidays[i][1];
      break;
    }
  }
  if( strOutput !== "" ){
    engine.log( " AutomatedTask RuntimeLog:" +
                " Holiday" );
  }else{
    engine.log( " AutomatedTask RuntimeLog:" +
                " Not holiday" );
    for( let i = 0; i < arr2dClosedDays.length; i++ ){
      if( arr2dClosedDays[i][0] === dateTarget.getDay() + "" ){ // Matches Closing Day
        engine.log( " AutomatedTask RuntimeLog: " +
                    arr2dClosedDays[i][1] );
        let boolOpen = false;
        if( strOpenDespiteWeekends !== "" ){
          for( let j = 0; j < arrOpenDespiteWeekends.length; j++ ){
            let dateTmp = parseDateAsMidnight( arrOpenDespiteWeekends[j] );
            if( dateTmp.getTime() === dateTarget.getTime() ){
              boolOpen = true;
              break;
            }
          }
        }
        if( ! boolOpen ){
          strOutput = arr2dClosedDays[i][1];
        }else{
          engine.log( " AutomatedTask RuntimeLog:" +
                      " but be Open despite " + arr2dClosedDays[i][1] );
        }
        break;
      }
    }
  }

  if( numToAdd < 0 ){
    if( strOutput === "" ){
      n = n - 1;
    }else{
      strNonbiz += dateTarget.toBpmsYMD() + "\t" + strOutput + "\n";
    }
  }
  if( numToAdd > 0 ){
    if( strOutput === "" ){
      n = n + 1;
    }else{
      strNonbiz += dateTarget.toBpmsYMD() + "\t" + strOutput + "\n";
    }
  }
} // end of while

if( strNonbiz !== "" ){
  strNonbiz = strNonbiz.slice( 0, -1 ); // delete last "\n"
}


//// == Data Updating / ワークフローデータへの代入 ==
engine.setData( datePocketOutput, new java.sql.Date( dateTarget.getTime() ) );
if( strPocketNonbiz !== null ){
  engine.setData( strPocketNonbiz, strNonbiz );
}


} //////// END "main()" /////////////////////////////////////////////////////////////////


function parseDateAsMidnight( str ) { // Transform date from String to Date
  if( str === "" ){
    throw new Error( "\n AutomatedTask ParseDateError:" +
                     " String is empty \n" );
  }
  var arrNumParts = str.match( /\d+/g );
  if( arrNumParts === null ){
    throw new Error( "\n AutomatedTask ParseDateError:" +
                     " No numeric characters in: " + str + "\n" );
  }
  if( arrNumParts.length < 3){
    throw new Error( "\n AutomatedTask ParseDateError:" +
                     " 3 Parts of numeric characters are needed in: " + str + "\n" );
  }
  return new Date( parseInt(arrNumParts[0], 10), 
                   parseInt(arrNumParts[1], 10) - 1, 
                   parseInt(arrNumParts[2], 10) ); // months are 0-based
  // Note: new Date("2014-11-10") // Mon Nov 10 2014 09:00:00 GMT+0900 (JST)
  // Note: new Date(2014, 10, 10) // Mon Nov 10 2014 00:00:00 GMT+0900 (JST)
}

// Parses CSV string and Creates a 2D array.
function parseTwoColOfCsv( strCsv ){
  const arrCsv = strCsv.split("\n");
  let arr2d    = [];
  for( let i = 0; i < arrCsv.length; i++ ){
    if( arrCsv[i] === "" ){ continue; } // Skip blank lines
    let arrTmp = [];
    let arrCells = arrCsv[i].split(",");
    for( let j = 0; j < arrCells.length; j++ ){
      arrTmp[0] = arrCells[0];
      arrTmp[1] = arrCells[1];
      // Refer to only the first two columns of CSV.
    }
    arr2d.push( arrTmp );
  }
  return arr2d;
}
/*

Notes:
- When the process reaches the automated task, N business days after the DATE data is automatically calculated.
    - `YYYY-MM-DD` (date type format), `YYYY/MM/DD`, etc.
    - `YYYY-MM-DD hh:mm` (datetime data format) can also be regarded as the starting date.
- If it corresponds to "A1. Holiday list" of the config value, the name of the holiday is stored.
    - You need to list all the holidays of all years.
        - It is also possible to add "company-defined holidays" to the holiday list.
    - The CSV file in the Cabinet Office "About National Holidays" is useful. (JAPAN)
        - https://www8.cao.go.jp/chosei/shukujitsu/gaiyou.html
        - https://www8.cao.go.jp/chosei/shukujitsu/syukujitsu.csv
        - 2022/1/1,New Year's Day 2022/1/10,Coming-of-Age Day 2022/2/11,National Foundation Day 2022/2/23,Emperor's Birthday
- If the config value "B1. Regular closing days" is applicable, the day name is stored.
    - However, if it corresponds to "a day that is not closed specially", an empty string will be stored.
    - Set the regular holidays with a number from 0-6. (Sunday-Saturday: 0-6)
    - If it falls on a national holiday, it will not be judged whether it is a regular closing day.
- The starting date is not subject to holiday judgment.

APPENDIX:
- Refers to only the first two columns of CSV.
    - No error will occur even if the memo is written in the third column.
    - (1st row, 2nd row, remarks memo)
- No problem if the data is for holidays of about 50 years (about 1000 lines).
    - If you set a large number of "A1. Holiday list" that exceeds 10,000 lines, an error may occur.

Notes-ja:
- 案件が自動処理工程に到達した際、日付データのN営業日後が自動計算されます。
    - `YYYY-MM-DD` (日付型データの書式) や `YYYY/MM/DD` など
    - `YYYY-MM-DD hh:mm` (日時型データの書式) も起点日と見なすことが可能です。
- コンフィグ値の「A1.祝祭日リスト」に該当する場合に、祝祭日の名前が格納されます。
    - 全ての年の全ての祝祭日をリストアップする必要があります。(実務上必要とされる範囲)
        - 祝祭日リストに「会社が定めた休日」を追加することも可能です。
    - 内閣府「国民の祝日について」にあるCSVファイルが有用です。
        - https://www8.cao.go.jp/chosei/shukujitsu/gaiyou.html
        - https://www8.cao.go.jp/chosei/shukujitsu/syukujitsu.csv
        - 2022/1/1,元日 2022/1/10,成人の日 2022/2/11,建国記念の日 2022/2/23,天皇誕生日
- コンフィグ値「B1.定休日(休業する曜日)」に該当する場合に、曜日名が格納されます。
    - ただし「特別に休業としない日」に該当した場合には、空文字が格納されます。
    - 定休曜日は 0-6 の数字で設定します。(Sunday - Saturday : 0 - 6)
    - 祝祭日に該当する場合、定休日判定は行われません。
- 起点日は祝日判定の対象にはなりません。

APPENDIX-ja:
- CSV設定は、先頭の2列だけが参照されます。
    - 3列目にメモが記載されていてもエラーにはなりません
    - (1列目,2列目,備考メモ)
- 50年程度の祝祭日データ(1000行程度)であれば問題ありません。
    - 1万行を超えるような大量の「A1.祝祭日リスト」を設定するとエラーとなる場合があります。
*/

Download

2021-10-04 (C) Questetra, Inc. (MIT License)
https://support.questetra.com/addons/date-calc-after-n-business-days-2021/
The Add-on import feature is available with Professional edition.
Freely modifiable JavaScript (ECMAScript) code. No warranty of any kind.

Notes

  • When the process reaches the automated task, N business days after the DATE data is automatically calculated.
    • YYYY-MM-DD (date type format), YYYY/MM/DD, etc.
    • YYYY-MM-DD hh:mm (datetime data format) can also be regarded as the starting date.
  • If it corresponds to “A1. Holiday list” of the config value, the name of the holiday is stored.
  • If the config value “B1. Regular closing days” is applicable, the day name is stored.
    • However, if the day falls on a day that is not a special holiday an empty string will be stored.
    • Set the regular holidays with a number from 0-6. (Sunday-Saturday: 0-6)
    • If it falls on a national holiday, it will not be judged whether it is a regular closing day.
  • The starting date is not subject to holiday judgment.

Capture

Calculates N business days after the specified date. If N is negative, returns N business days ago. It is necessary to set a holiday in advance. It is also possible to add company-original holidays.
Calculates N business days after the specified date. If N is negative, returns N business days ago. It is necessary to set a holiday in advance. It is also possible to add company-original holidays.

Appendix

  • Refers to only the first two columns of CSV.
    • No error will occur even if the memo is written in the third column.
    • (1st row, 2nd row, remarks memo)
  • No problem if the data is for holidays of about 50 years (about 1000 lines).
    • If you set a large number of “A1. Holiday list” that exceeds 10,000 lines, an error may occur.

See also

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: