自动计算请假时长、天数
使用技术:
- vue
- vant-DatetimePicker
- moment.js
效果(完全体):
- 按小时请假:
- 按天请假:
规则:
- 按小时请假中,不足0.5小时的算0.5小时。要去除午休和工作时间之外的小时。
- 按天请假中,不足0.5天的按0.5天计算。要去除周六、周日。
事情是这样的
业务上碰到个申请请假的功能,用户选择请假的开始时间和结束时间,然后自动计算出时长。
有两种请假方式:按小时/按天。
先不考虑其他的
vant的DatetimePicker组件中,可以获取选中的时间。
moment.js的duration().asDays()和duration().asHours()可以获取两个日期之间相差的天数/小时。
那么怎么实现这个功能很明显了:
-
获取开始时间和结束时间
//dom <van-datetime-picker ...其他参数 @confirm="onConfirm" /> //js onConfirm(value){ value:你选中的时间 }
-
获取时间差 结束-开始
let result = eTime-Stime
-
根据请假类型将时间差转换为对应单位
moment.duration(result).asDays()
-
做一下数据处理
//分离整数和小数 let floatNum = days%1 let intNum = days-floatNum //0.5 days = intNum + (floatNum > 0.5 ? 1 : floatNum === 0 ? 0 : 0.5)
-
将结果显示在页面上
那这边有个新需求
是的,当差不多写完还在觉得沾沾自喜时。产品告诉我,只计算小时和天数太不合理了,需要结合工作时间来考虑。
比如X公司的工作时间是:
周一至周五:
8:00——18:00
其中12:00——14:00 为午休时间,不计入工时
周六周日为休息日,不计入天数
(下文都会以X公司为讨论标准。)
那么,选择了按小时请假的小明请了周一8点到周二8点的假,此时请假时长应为8小时。而选择了按天请假的小红请了周四到周一的假,此时请假天数应为3天。
按小时请假
参考上面的规则。
有这么几种情况:
- 同一天请假
- 是正常工作日请假
- 是节假日请假(
为什么要如此测试我的程序?)
- 不在同一天请假
- 不包含节假日
- 包含了节假日(
能不能请两次)
通过getDate()判断一下两个日期是不是在同一天
let sday = sTime.getDate(),
eday = eTime.getDate()
if(sday === eday){
//大显身手
}
同一天请假
之正常工作日请假
需要做:
- 规范一下这两个日期,确保开始时间不小于8:00(开始工作时间),结束时间不小于18:00(结束工作时间)
- 时长结果需要减去午休时间,但还有这么些情况:
- 请假开始时间(简称sTime)在午休开始时间(简称breakStart)之前,请假结束时间(简称eTime)在午休结束时间(简称breakEnd)之后
- sTime在午休时间中,eTime在午休时间后
- sTime在午休时间开始之前,eTime在午休时间中
根据X公司的工作制度,午休时间为12:00——14:00两个小时。可以写出这些判断:
if(sTime>breakStart&&sTime<breakEnd){
return (breakEnd-sTime)
或规范sTime到breakEnd
}
if(eTime>breakStart&&eTime<breakEnd){
return (eTime-breakStart)
或规范eTime到breakStart
}
if(sTime<=breakStart&&eTime>=breakEnd){
return 2
返回一个标识符,提示-2
}
之非工作日请假
直接返回0好吧。
不在同一天请假
不包含节假日
将第一天和最后一天拆开。第二天至倒数第二天为全天请假。
- 第一天请假时长:当天下班时间 - sTime
- 最后一天请假时长:eTime - 当天上班时间
- 中间的天数*8
包含节假日
跟上面基本相同,需要剪掉包含节假日的天数。
根据上述分析,可以写出一个计算当天请假时长的函数。
其中standardTime是规范化时间的函数,返回一个在工作时间范围(除去午休)的小时数和分钟数
const standardTime = (hour, min) => {
let shour = hour,
smin = min
//规范开始工作时间和结束工作时间
smin = shour < WORK_START_TIME || shour > WORK_END_TIME ? 0 : smin
shour = shour < WORK_START_TIME ? WORK_START_TIME : shour > WORK_END_TIME ? WORK_END_TIME : shour
//当时间在午休时间内的情况
smin = shour >= BREAK_START && shour < BREAK_END ? 0 : smin
shour = shour >= BREAK_START && shour <= BREAK_END ? BREAK_END : shour
return { shour, smin }
}
//按小时请假,确保sTime和eTime在同一天,且当天不是节假日
const leaveByhours = (sTime, eTime) => {
let hours = 0,result = null
let sday = sTime.getDate(),shour = sTime.getHours(),
smin = sTime.getMinutes()
let eday = eTime.getDate(),ehour = eTime.getHours(),
emin = eTime.getMinutes()
let stTime = standardTime(shour, smin)
let etTime = standardTime(ehour, emin)
result =
new Date(eTime.getFullYear(), eTime.getMonth(), eday, etTime.shour, etTime.smin) -
new Date(sTime.getFullYear(), sTime.getMonth(), sday, stTime.shour, stTime.smin)
hours = moment.duration(result).asHours()
//是否包含午休
hours -= (stTime.shour <= BREAK_START && etTime.shour >= BREAK_END)?BREAK_TIME:0
let { intNum, floatNum } = separation(hours)
hours = intNum + (floatNum > 0.5 ? 1 : floatNum === 0 ? 0 : 0.5)
return hours
}
接下来写一个计算请假天数的函数。
- 获取开始时间和结束时间的时间差(单位:天)
- 把天数作为一个数值数组遍历,判断当天是否是非工作日。
- 结果为时间差-非工作日的天数
小花是个时间观念很差的人他提交了一个周五10:00到周日10:00的请假。按照上述所说的计算时长的方式,会先计算周五当天的请假(6个小时),再计算周日的请假(因为是节假日所以是0),最后计算周六一天(因为是节假日所以是1-1天)。所以小花的请假时长应该为6小时。
小绿也是一个时间观念很差的人。他选择了周五10:00到周一10:00请假。跟小花差不多,他的请假时长是周五的6小时,加上周一的2小时,一共8个小时。
使用Date().getDay()来判断是否是周六日
const checkDay = time => {
let flag = false
let day = time.getDay()
flag = day === 0 || day === 6 ? true : false
return flag
}
const leaveBydays = (sTime, eTime) => {
let sday = sTime.getDate()
let result = eTime - sTime
let days = moment.duration(result).asDays()
let { intNum, floatNum } = separation(days)
let weekdays = 0
for (let i = 0; i < Math.round(days); i++) {
let nowDay = new Date(new Date(sTime).setDate(sday + i))
if (checkDay(nowDay)) {
weekdays++
}
}
days = intNum + (floatNum > 0.5 ? 1 : floatNum === 0 ? 0 : 0.5)
days -= weekdays
return days
}
那么按小时请假,就可以写成这样
const calcHour = (sTime, eTime) => {
let hours = 0,
sday = sTime.getDate(),
eday = eTime.getDate()
if (sday === eday) {
//1.同一天请假
hours = checkDay(sTime) ? 0 : leaveByhours(sTime, eTime)
} else {
//2.不同天请假
let e1 = new Date(new Date(sTime).setHours(WORK_END_TIME + 1)) //设置超过范围的数,触发初始化
let s2 = new Date(new Date(eTime).setHours(WORK_START_TIME - 1))
let ds = new Date(sTime.getFullYear(), sTime.getMonth(), sTime.getDate() + 1, 0, 0),
de = new Date(eTime.getFullYear(), eTime.getMonth(), eTime.getDate(), 0, 0)
let d = leaveBydays(ds, de)
let r1 = checkDay(sTime) ? 0 : leaveByhours(sTime, e1)
let r2 = checkDay(s2) ? 0 : leaveByhours(s2, eTime)
hours = r1 + d * WORK_TIME + r2
}
return hours < 0 ? 0 : hours
}
按天请假
同一天请假
直接返回0好吧
不在同一天请假
之不包含节假日
- 用momen的asDays
之包含节假日
- 减去节假日的部分
那其实和按小时请假时用的leaveBydays差不多。只不过要算到请假结束当天。
我直接魔改
leaveBydays = (sTime, eTime, complement = 0)
let days = moment.duration(result).asDays() + complement
const calcDay = (sTime, eTime) => {
let days = leaveBydays(sTime, eTime, 1)
return days
}
完善
节假日怎么算?先想想,没准不需要这个功能呢。
节假日可以看成是多出来的周六周日。
包装一下日期对象。
const dateObject = {
date:Date对象,每一天的0:00
isHoliday:true/false
}
获取一个节假日数组,里面包含了今年所有放假的日子。(或许已经有人写过了,节假日API?)
然后将今年的每一天,都生成一个dateObject。
checkDay函数改成判断dateObject.isHoliday。
其他
js部分代码已经上传啦,点这里。