js|请假时长、天数自动计算

3,496 阅读5分钟

自动计算请假时长、天数

使用技术:

  • vue
  • vant-DatetimePicker
  • moment.js

效果(完全体):

  • 按小时请假:

按小时请假.gif

  • 按天请假:

按天请假.gif

规则:

  • 按小时请假中,不足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部分代码已经上传啦,点这里