[js] ManDay 계산
January 11, 2019 - [js]
어떤 작업을 수행했던 시작과 끝 시간이 주어지고 해당 기간에 소요된 공수를 일단위로 계산한 결과가 필요할 때 아래와 같이 계산할 수 있 다
요건
- 주5일(월~금) 하루 8시간 근무를 기준으로 계산한다
- 점심시간(12~13시) 한 시간은 계산시 제외한다
- 시작일과 끝일은 분단위까지 계산에 반영한다
- 결과값은 수수점 셋째자리에서 올림처리하고 소수점 둘째 자리까지 표현한다
- 입력값은 yyyyMMddHHmm 포맷의 12자리 문자열이다
코드
function calcManDay(st, ed) {
// date 기준 diff 일수 이후 날짜 계산
function addDays(date, diff){
const oneday = 60 * 1000 * 60 * 24;
return new Date(date.getTime() + oneday * diff);
}
// 시작일자가 토/일 이면 앞선 금요일로 보정한다
function setStartDate(date){
if(date.getDay()===0){
// 일요일이면 이틀 전으로 땡겨서 계산
return addDays(date1, -2)
}else if(date.getDay()===6){
// 토요일이면 하루전으로 땡겨서 계산
return addDays(date1, -1)
}else {
return date;
}
}
// 끝 일자가 토/일 이면 다음 월요일로 보정한다
function setEndDate(date){
if(date.getDay()===0){
// 일요일이면 하루 뒤로 미뤄서 계산
return addDays(date, 1)
}else if(date.getDay()===6){
// 토요일이면 이틀 뒤로 미뤄서 계산
return addDays(date, 2);
}else{
return date;
}
}
// 시작일자와 날짜차이를 입력받아서 토/일요일을 일수에서 차감한다.
function workingDayCnt(date, dayDiff){
var fullWeekCnt = Math.floor(dayDiff / 7);
var restDayCnt = dayDiff % 7;
if (date.getDay() + restDayCnt <= 5) {
return fullWeekCnt * 5 + restDayCnt;
} else {
return fullWeekCnt * 5 + restDayCnt - 2;
}
}
// date1 과 date2의 일수차이를 계산
function dayDiff(date1, date2){
// 시작일과 끝일이 주말일 경우 날짜 보정
date1 = setStartDate(date1);
date2 = setEndDate(date2);
var timeDiff = Math.abs(date2.getTime() - date1.getTime());
return Math.ceil(timeDiff / (1000 * 3600 * 24));
}
// 시작일자와 끝일자에서 소요된 시간을 분단위로 계산해서 ManDay를 계산한다
function workingHour(workingDayCnt, HH1, mm1, HH2, mm2){
var hh1 = Number(HH1);
var mm1 = Number(mm1);
if(hh1 < 9){
// 9시 이전으로 등록된 시각은 9로 세팅
hh1 = 9;
mm1 = 0;
}
var hh2 = Number(HH2);
var mm2 = Number(mm2);
if(hh2 >= 18){
// 18시 이후로 처리된 건은 모두 18시로 세팅
hh2 = 18;
mm2 = 0;
}
// 점심시간 차감
var h1 = hh1 >= 13 ? (hh1 - 1) : hh1;
var h2 = hh2 >= 13 ? (hh2 - 1) : hh2;
// 하루 8시간 근무 기준으로
if (workingDayCnt === 0) {
return ((h2 - h1 - 1)*60 + (60-mm1) + mm2) / (8*60);
}else{
return ((17 - h1 -1 )*60 + (60-mm1) + (h2 - 9)*60 + mm2) / (8*60);
}
}
var yyyy1 = st.substr(0, 4);
var MM1 = st.substr(4, 2);
var dd1 = st.substr(6, 2);
var HH1 = st.substr(8, 2);
var mm1 = st.substr(10, 2);
var yyyy2 = ed.substr(0, 4);
var MM2 = ed.substr(4, 2);
var dd2 = ed.substr(6, 2);
var HH2 = ed.substr(8, 2);
var mm2 = ed.substr(10, 2);
var date1 = new Date(MM1 + "/" + dd1 + "/" + yyyy1);
var date2 = new Date(MM2 + "/" + dd2 + "/" + yyyy2);
var dayDiff = dayDiff(date1, date2);
var wd = workingDayCnt(date1, dayDiff);
var wh = workingHour(wd, HH1, mm1, HH2, mm2);
var res;
if(wd <= 1){
res = wh;
}else{
res = wd -1 + wh;
}
// 소수점 셋째 자리에서 올림처리하고 소수점 두째자리까지 표현
return Math.ceil(res * 100) / 100;
}
테스트코드
var arr = [{"seq":0,"start":"201901101029","end":"201901101053"},{"seq":1,"start":"201901100959","end":"201901101417"},{"seq":2,"start":"201901091701","end":"201901091802"},{"seq":3,"start":"201901091612","end":"201901091612"},{"seq":4,"start":"201901081818","end":"201901081818"},{"seq":5,"start":"201901091105","end":"201901091700"},{"seq":6,"start":"201901091047","end":"201901091559"},{"seq":7,"start":"201901091029","end":"201901091046"},{"seq":8,"start":"201901090956","end":"201901110932"},{"seq":9,"start":"201901090938","end":"201901090941"},{"seq":10,"start":"201901090927","end":"201901090936"},{"seq":11,"start":"201901071640","end":"201901071710"},{"seq":12,"start":"201901041605","end":"201901041640"},{"seq":13,"start":"201901040910","end":"201901041437"},{"seq":14,"start":"201901021000","end":"201901031713"},{"seq":15,"start":"201901031145","end":"201901031623"},{"seq":16,"start":"201901021118","end":"201901101432"},{"seq":18,"start":"201812311414","end":"201812311416"},{"seq":19,"start":"201812311322","end":"201901041348"},{"seq":20,"start":"201812311010","end":"201812311055"},{"seq":21,"start":"201812271035","end":"201812281339"},{"seq":22,"start":"201812271558","end":"201812271652"},{"seq":23,"start":"201812271453","end":"201812271501"},{"seq":24,"start":"201812271335","end":"201812271357"},{"seq":25,"start":"201812261735","end":"201812261736"},{"seq":26,"start":"201812261634","end":"201812261643"},{"seq":27,"start":"201812261032","end":"201812261634"},{"seq":28,"start":"201812241119","end":"201812241121"},{"seq":29,"start":"201812211616","end":"201812271414"}];
arr.forEach(v => {
function format(str){
return str.substr(0,4) + "/" + str.substr(4,2) + "/" + str.substr(6,2) + " " + str.substr(8,2) + ":" + str.substr(10,2)
}
console.log(format(v.start) + " ~ " + format(v.end) + " : " + calcManDay(v.start, v.end))
})
출력결과
2019/01/10 10:29 ~ 2019/01/10 10:53 : 0.05
2019/01/10 09:59 ~ 2019/01/10 14:17 : 0.42
2019/01/09 17:01 ~ 2019/01/09 18:02 : 0.13
2019/01/09 16:12 ~ 2019/01/09 16:12 : 0
2019/01/08 18:18 ~ 2019/01/08 18:18 : 0
2019/01/09 11:05 ~ 2019/01/09 17:00 : 0.62
2019/01/09 10:47 ~ 2019/01/09 15:59 : 0.53
2019/01/09 10:29 ~ 2019/01/09 10:46 : 0.04
2019/01/09 09:56 ~ 2019/01/11 09:32 : 1.95
2019/01/09 09:38 ~ 2019/01/09 09:41 : 0.01
2019/01/09 09:27 ~ 2019/01/09 09:36 : 0.02
2019/01/07 16:40 ~ 2019/01/07 17:10 : 0.07
2019/01/04 16:05 ~ 2019/01/04 16:40 : 0.08
2019/01/04 09:10 ~ 2019/01/04 14:37 : 0.56
2019/01/02 10:00 ~ 2019/01/03 17:13 : 1.78
2019/01/03 11:45 ~ 2019/01/03 16:23 : 0.46
2019/01/02 11:18 ~ 2019/01/10 14:32 : 6.28
2018/12/31 14:14 ~ 2018/12/31 14:16 : 0.01
2018/12/31 13:22 ~ 2019/01/04 13:48 : 4.06
2018/12/31 10:10 ~ 2018/12/31 10:55 : 0.1
2018/12/27 10:35 ~ 2018/12/28 13:39 : 1.39
2018/12/27 15:58 ~ 2018/12/27 16:52 : 0.12
2018/12/27 14:53 ~ 2018/12/27 15:01 : 0.02
2018/12/27 13:35 ~ 2018/12/27 13:57 : 0.05
2018/12/26 17:35 ~ 2018/12/26 17:36 : 0.01
2018/12/26 16:34 ~ 2018/12/26 16:43 : 0.02
2018/12/26 10:32 ~ 2018/12/26 16:34 : 0.63
2018/12/24 11:19 ~ 2018/12/24 11:21 : 0.01
2018/12/21 16:16 ~ 2018/12/27 14:14 : 3.75
제약사항
- 토,일 뿐 아니라 법정공휴일도 제외할 수 있으면 좋은데 거기까지는 처리를 못했다.