今回は、開始日付と終了日付から、その期間の稼働日数(営業日数)を算出してみます。ロジックを考えることがメインですが、コードはJavaScriptで記載します。
算出するための条件
稼働日数を算出するのに、土日など週の中での定休日と祝日について考慮する必要があります。今回は以下の条件で考えてみます。
- 週の定休日となる曜日を自由に指定できる。
- 祝日となる日付を自由に指定できる。
今回は開始日付が終了日付以下の関係が担保された状態のものとし、このチェックロジックは含めません。
ロジックの検討
開始日付と終了日付からの単純な期間を求める場合、JavaScriptではDateオブジェクトのタイムスタンプの差分で算出します。開始日付と終了日付が同じ場合、1日と考えると以下のようになります。
var beginDate = new Date('2018/08/01'); var endDate = new Date('2018/08/02'); //開始と終了日付のタイムスタンプ var beginTime = beginDate.getTime(); var endTime = endDate.getTime(); //時間差を算出 var diff = endTime - beginTime; //時間差をミリ秒単位を日単位に変換し(切り捨て)、1日分を加算 var term = Math.floor(diff / (24 * 60 * 60 * 1000)) + 1;
開始日付から終了日付の間の各日付について、定休日となる曜日か、または祝日かを判定していくことで稼働日数を算出することも可能ですが、無駄な部分も多いので定休日の判定と祝日の判定を別けて対応します。これらを大きく別けると以下のようになります。
- 開始日付と終了日付の期間となる日数を算出する(上記で算出済み)。
- 対象期間の中で定休日となる日数を算出する。
- 対象期間の中で祝日となる日数を算出する。
- 対象期間から定休日となる日数と祝日となる日数を差し引いて、稼働日数を算出する。
定休日となる日数を算出する
ここでの定休日は曜日を指定してのものです。例えば土日などです。この指定した曜日の数が、一週間での定休日の日数となります。ある期間に含まれる週の数と一週間の定休日数を掛ければ簡単に求められことになります。ただし、割り切れない余りについては、個別に判定する必要があります。これらを順に行っていきます。
- 一週間の定休日の日数を算出する。
- 対象期間に含まれる週数を算出して、週数分の定休日の日数を算出する。
- 対象期間の中で上記の週数に含まれない余りを算出して、その余りの日数に含まれる定休日の日数を算出する。
JavaScriptではDateオブジェクトの曜日は、0が日曜日で順に連番され、6が土曜日という数値になります。定休日となる設定はこれらの数値を配列で保持しているものとします。
//定休日となる曜日の設定 var regularDayOff = [0,6]; //一週間の定休日の数 var lenDayOff = regularDayOff.length;
上で算出している開始日付から終了日付の対象期間を7で割った商がその期間に収まる週の数となります。そして余りの日数については、日毎に判定する必要があります。今回は余りが出た場合は開始日付から、その余りの日数分だけ曜日の判定を行うようにしてみます。(終了日付から、その余りの日数分だけ戻って判定しても良い)
//期間に入る週の数、7で割って切り捨てて算出する var weeks = Math.floor(term / 7); //週の数から、その期間の定休日の休日数を算出する var dayOffs = weeks * lenDayOff;
後は余り日数の部分を開始日付から曜日の判定を行います。余りと曜日の判定は剰余演算子を使用します。JavaScriptでは「%」です。
//週の数に入らない余りの日数 var remainderDays = term % 7; //余りの日の処理 if(remainderDays>0){ //余りの日がある場合、開始日付から余りの日数だけ曜日が定休日かの判定を行う //開始日付の曜日数値の取得 var beginDay = beginDate.getDay();//0~6の曜日数値 for(var i = 0; i < remainderDays; i++){ //曜日数値に余りの日数を加算していき、7で割った余りの曜日数値が定休日の配列に含まれるか if(regularDayOff.indexOf((beginDay + i) % 7) != -1){ //定休日の配列に含まれる場合、休日数に加算する dayOffs++; } } };
これで、対象期間に含まれる定休日の日数が算出できました。
祝日となる日数を算出する
続いて対象期間に含まれる祝日の日数を算出します。祝日は自由に指定できる想定ですが、それらを配列で設定したものとします。まず祝日の一覧と開始日付、終了日付を比較できるようにするためタイムスタンプに変換します。そのタイムスタンプと開始日付と終了日付を比較して日数を算出します。
- 祝日の一覧をタイムスタンプに変換する。
- 祝日の一覧から開始日付のタイムスタンプより大きく、終了日付のタイムスタンプより小さい祝日を探し、カウントする。
祝日は「YYYY/MM/DD」の形式で配列で保持しているものとします。
//祝日の設定 var holidayDates = ['2018/01/01','2018/01/02','2018/01/09'];
この祝日の一覧をタイムスタンプに変換するのですが、いくつか処理を検討します。まずこの祝日の一覧ですが、ソートされていない状態のもであればソートします。ソートしておくことで日付の比較を行う際に終了日付より大きくなったところで、それ以後は比較する必要がなくなります。
続いて祝日と定休日の問題です。祝日の中に定休日となる曜日の日付が含まれている場合、定休日の処理と合わせてカウントされてしまいます。よって定休日の曜日に含まれる日付について除いたものだけをタイムスタンプにした配列を作成して比較に使用します。
//祝日をソートする holidayDates.sort(); //祝日のタイムスタンプの配列 var holidayTime=[]; for(var i = 0; i < holidayDates.length; i++){ //日付オブジェクトを設定する var dateObj = new Date(holidayDates[i]); if(regularDayOff.indexOf(dateObj.getDay())==-1){ //定休日の曜日に存在しない場合、祝日のタイムスタンプの配列に追加する holidayTime.push(dateObj.getTime()); } }
祝日のタイムスタンプ一覧ができたら、日付の比較を行いカウントします。祝日のタイムスタンプ一覧から、開始日付のタイムスタンプより大きい祝日があったら以後は終了日付のタイムスタンプより小さい祝日をカウントすることで祝日の日数を求めることができます。
//祝日のカウント用 var holidays=0; //開始日付より大きいかのフラグ var flgBegin=false; for(var i = 0; i < holidayTime.length; i++){ //開始日付のフラグにより処理を変える if(flgBegin == false){ //開始日付より大きくない状態 if(beginTime <= holidayTime[i]){ //祝日が開始日付より大きい場合、フラグを設定する flgBegin=true; if(holidayTime[i] <= endTime){ //終了日付より小さい場合、対象期間の範囲内なので加算する holidays++; }else{ //終了日付より大きい場合、対象期間の範囲外なのでループを抜け出す break; } } }else{ //開始日付より大きい状態 if(holidayTime[i] <= endTime){ //終了日付より小さい場合、対象期間の範囲内なので加算する holidays++; }else{ //終了日付より大きい場合、対象期間の範囲外なのでループを抜け出す break; } } }
以上で対象期間とその期間に含まれる定休日の日数と祝日の日数の算出ができました。後は差引した結果が稼働日数となります。
//稼働日数の算出 var workingDay = term - dayOffs - holidays;
稼働日数の算出ができたので利用しやすい形式に関数などを設定して下さい。
まとめ
今回は稼働日数(営業日数)の算出ロジックを考えてみました。
以上