JavaScript实现请假时间计算

相信在座的各位都有在公司的审批系统上请过假,因为我国的四大发明之一:调休,所以这个请假时间的计算并不是能够直接算出来的,正巧这几天写了这个代码,在此想与各位分享一下。

这几日正在备考软件设计师,稍稍学习了一下设计模式,因此我左思右想,我认为应该是可以用到一个设计模式的,于是我满怀壮志,打下第一句代码"abstract",vscode开始提示仅TypeScript支持,无奈,只能老老实实写了。

首先设计这个请假时间,我们要想想为什么需要专门来写这个代码,啊!因为调休!调休就造成了以下四种场景:

  1. 工作日需要上班(正常情况)
  2. 工作日不需要上班(那就是放假)
  3. 周六日不需要上班(正常情况,也不能排除有的勇士是996或007,该代码也能兼容好吧,无惧996007)
  4. 周六日需要上班(常规情况下我们称为调休,自己申请加班不算哈)

正因为调休,我们不能直接硬生生把星期六日排除掉,因此我本人第一个不支持调休!

那么要解决这四种情况,身为小腊鸡的我只能想出来if else了,如果有更好的方法,希望大家可以评论告诉我,那么if else也得写的高端,肯定不能一个个来对比,那就会写的很长,那么废话不多说虽然已经说了很多,我们继续往下说。

我先声明两个数组,一个是special_workingDate,一个是special_holidayDate,为什么special,因为调休,它才会变得special,举个例子

//中秋假期与国庆假期
const special_holidayDate= ["2023-09-29","2023-09-30","2023-10-01","2023-10-02","2023-10-03","2023-10-04","2023-10-05","2023-10-06"]
//国庆后的调休
const special_workingDate = ["2023-10-07","2023-10-08"]

然后,我们按照正常的逻辑,写个if else

if(){//今天上班

}else{//今天周末
    
}

这里考虑到有些喜欢工作的勇士一周需要上多一天班,我们可以这么写这个上班的if

workingDay = [1,2,3,4,5,6]

if(workingDay.includes( startTime.getDay() ) ){
    //如果我传入的日期,在自己规定的workingDay里,那今天就是得上班
}else{
    //如果我传入的日期,不在workingDay里,那今天就休息
}

有了这个逻辑,我们再结合上面的调休,再深入的写个if else

//中秋假期与国庆假期
const special_holidayDate= ["2023-09-29","2023-09-30","2023-10-01","2023-10-02","2023-10-03","2023-10-04","2023-10-05","2023-10-06"]
//国庆后的调休
const special_workingDate = ["2023-10-07","2023-10-08"]

workingDay = [1,2,3,4,5,6]
oneDayMS = 86400000//一天的毫秒数

function fn(){
    do{
        if(workingDay.includes( currentTime.getDay() ) ){
            //如果我传入的日期,在自己规定的workingDay里,那今天就是得上班
            if(calculate_WorkingDay_to_Holiday(currentTime)){
                //但如果今天是放假日
                currentTime = currentTime + oneDayMS//currentTime实际上我是用时间戳写的
                continue//因为我这写了个循环,平时请假肯定不止请一天嘛
                //如果是放假,就跳过这天,进入下一个循环
            }
        }else{
            //如果我传入的日期,不在workingDay里,那今天就休息
            if( !this.calculate_Holiday_to_WorkingDay( currentTime) ){
                //并且这个休息日,不是调休日,那就直接躺平好吧,休息!
                currentTime = currentTime + oneDayMS//currentTime实际上我是用时间戳写的
                continue//因为我这写了个循环,平时请假肯定不止请一天嘛
            }
        }
        //主要逻辑已经写完,接下来就是自由发挥时间
        //统计请假多少天,多少小时,多少分钟,多少秒,自行发挥
        
    }while(currentTime < endTime)
}

//是否在工作日放假
calculate_WorkingDay_to_Holiday(currentDate){
    //法定假日是否包含当前日期
    return special_holidayDate.includes( formatTime(currentDate) )
}

    //是否在节假日工作
calculate_Holiday_to_WorkingDay(currentDate){
    //法定工作日里是否包含当前日期
    return special_workingDate.includes( formatTime(currentDate) )
}

formatTime(date,j='-'){
    const year = date.getFullYear()
    const month = date.getMonth() + 1
    const day = date.getDate()
        
    function formatNumber(n){
        n = n.toString()
        return n[1] ? n : '0' + n
    }

    return [year, month, day].map(formatNumber).join(j)
}

 大体的思路就是这样,此外我还另设了一些参数,可供各位勇士在各种上班日上下班时间,能够准确的计算出准确的数据,我就直接贴代码了,讲太多了

class AskForLeave{
    //工作日,星期一二三四五
    workingDay = [1,2,3,4,5]
    //上午上下班时间
    workingTime_morning = '09:00'
    colsingTime_morning = '12:00'
    //下午上下班时间
    workingTime_afternoon = '13:45'
    closingTime_afternoon = '17:45'

    oneDayWorkTime
    
    oneDayMS = 86400000

    //特殊上班日
    special_workingDate

    //特殊放假日
    special_holidayDate

    timeStamp = new Date()

    days
    minutes
    hours
    totalMinutesCount = 0//总分钟数
    
    constructor(opt){
        this.initData(opt)
    }

    //初始化数据
    initData(opt){
        this.workingDay = opt.workingDay == undefined ? this.workingDay : opt.workingDay

        this.workingTime_morning = opt.workingTime_morning == undefined ? this.workingTime_morning : opt.workingTime_morning
        this.colsingTime_morning = opt.colsingTime_morning == undefined ? this.colsingTime_morning : opt.colsingTime_morning

        this.workingTime_afternoon = opt.workingTime_afternoon == undefined ? this.workingTime_afternoon : opt.workingTime_afternoon
        this.closingTime_afternoon = opt.closingTime_afternoon == undefined ? this.closingTime_afternoon : opt.closingTime_afternoon

        this.special_workingDate = opt.special_workingDate
        this.special_holidayDate = opt.special_holidayDate

        this.oneDayWorkTime = ( Number(this.colsingTime_morning.split(':')[0]) - Number(this.workingTime_morning.split(':')[0]) ) + ( Number(this.closingTime_afternoon.split(':')[0]) - Number(this.workingTime_afternoon.split(':')[0]) )
    }


    formatTime(date,j='-'){
        const year = date.getFullYear()
        const month = date.getMonth() + 1
        const day = date.getDate()
        
        function formatNumber(n){
            n = n.toString()
            return n[1] ? n : '0' + n
        }

        return [year, month, day].map(formatNumber).join(j)
    }

    //是否在工作日放假
    calculate_WorkingDay_to_Holiday(currentDate){
        //法定假日是否包含当前日期
        return this.special_holidayDate.includes( this.formatTime(currentDate) )
    }

    //是否在节假日工作
    calculate_Holiday_to_WorkingDay(currentDate){
        //法定工作日里是否包含当前日期
        return this.special_workingDate.includes( this.formatTime(currentDate) )
    }

    getDateObj(val){
        this.timeStamp.setTime(val)
        return this.timeStamp
    }

    DateDiffNoWeekDay(startTime, endTime) {
        if (startTime >= endTime) return 0;
        
        startTime = startTime.getTime()
        endTime = endTime.getTime()
        
        do {
            如果是上班日
            // console.log(`今天是${this.formatTime(this.getDateObj(startTime))},星期${this.getDateObj(startTime).getDay()}`)
            if(this.workingDay.includes(this.getDateObj(startTime).getDay())){
                //且是法定调休假日
                if( this.calculate_WorkingDay_to_Holiday(this.getDateObj(startTime)) ){
                    // console.log('今天是在工作日的放假日')
                    startTime = startTime + this.oneDayMS
                    startTime = this.getDateObj(startTime).setHours(Number(this.workingTime_morning.split(':')[0]))
                    startTime = this.getDateObj(startTime).setMinutes(Number(this.workingTime_morning.split(':')[1]))
                    continue
                }
            }else{//如果是周六日
                //且不是在周六日调休
                if( !this.calculate_Holiday_to_WorkingDay(this.getDateObj(startTime)) ){
                    //则认定为是一个普通周六日,直接跳过,初始化为早晨9点
                    // console.log('今天是个普通的周六日')
                    startTime = startTime + this.oneDayMS
                    startTime = this.getDateObj(startTime).setHours(Number(this.workingTime_morning.split(':')[0]))
                    startTime = this.getDateObj(startTime).setMinutes(Number(this.workingTime_morning.split(':')[1]))
                    continue
                }
            }
            // console.log('今天必须上班')
            //一切不需要上班的日期在上面均被跳过,接下来开始算需要上班的工时
            let tempMinutes = this.getDateObj(startTime).getHours() * 60 + this.getDateObj(startTime).getMinutes();
            //上午9点到12点半,算工时
            let tempMorningWorkingTime = Number(this.workingTime_morning.split(':')[0]) * 60 + Number(this.workingTime_morning.split(':')[1])
            let tempMorningClosingTime = Number(this.colsingTime_morning.split(':')[0]) * 60 + Number(this.colsingTime_morning.split(':')[1])
            let tempAfternoonWorkingTime = Number(this.workingTime_afternoon.split(':')[0]) * 60 + Number(this.workingTime_afternoon.split(':')[1])
            let tempAfternoonClosingTime = Number(this.closingTime_afternoon.split(':')[0]) * 60 + Number(this.closingTime_afternoon.split(':')[1])

            if (tempMinutes >= tempMorningWorkingTime && tempMinutes < tempMorningClosingTime ) {
                this.totalMinutesCount += 1;
            }

            if(tempMinutes >= tempMorningClosingTime && tempMinutes < tempAfternoonWorkingTime ){
                let tempObj = this.getDateObj(startTime)
                let currentTimeExcludeHours = new Date(`${tempObj.getFullYear()}-${tempObj.getMonth() + 1 > 10 ? tempObj.getMonth() + 1 : '0'+ (tempObj.getMonth() + 1) }-${tempObj.getDate() > 10 ? tempObj.getDate() : '0' + tempObj.getDate()} ${Number(this.workingTime_afternoon.split(':')[0])}:${Number(this.workingTime_afternoon.split(':')[1])}:00`)
                startTime = currentTimeExcludeHours.getTime()
                continue
            }   

            //下午13:45到17:45,算工时
            if (tempMinutes >= tempAfternoonWorkingTime && tempMinutes < tempAfternoonClosingTime ) {
                this.totalMinutesCount += 1;
            }

            startTime = startTime + 60 * 1000//每次增加一分钟
            
        } while ( startTime < endTime)
        this.days = Math.floor(this.totalMinutesCount / (this.oneDayWorkTime * 60))
        let hoursAndMinutes = this.totalMinutesCount % (this.oneDayWorkTime * 60)
        this.hours = Math.floor(hoursAndMinutes / 60)
        this.minutes = Math.floor(hoursAndMinutes % 60)

        let result = {
            totalMinutes:this.totalMinutesCount,
            days:this.days,
            hours:this.hours,
            minutes:this.minutes
        }
        return result;
    }
}
//调用方法如下
const askForLeave = new AskForLeave({
        special_workingDate: [],//法定调休日
        special_holidayDate: []//法定放假日
    })
//这里我就不传了,数据上面有

//调用内部计算请假方法
let res = askForLeave.DateDiffNoWeekDay(new Date("2023-9-25 09:00"), new Date("2023-10-10 17:30"))

//输出结果
console.log(res)

如果没设置调休日放假日,结果是这样的

2f08de60145d44a1af830aecb25eae3c.png

如果设置了,结果是这样的

65c41582709b455d848d7e2dcd3cdf0f.png

我封装成了class,大家可以自行配置一些参数

  1. 如上几天的班,这里按星期一二三四五六七来配置
  2. 上下班时间

6f1da154b5094892b25c9a5c34a1436f.png 

这些配置就能基本满足所有公司的请假时间计算了,我的while循环是基于时间戳的分钟计算的,因此每执行一次都会增加一分钟,直到与请假截止时间重合。

可以看到我频繁的调用了一个this.getDateObj()方法,因为while循环里用的是时间戳,时间戳并不是一个标准的时间对象,那么他就不具备时间对象的一些方法,getDate(),getFullYear(),getMonth(),getDay(),getMinutes(),getTime(),setHours(),setMinutes()等等,这里就不一一列举了(虽然已经列举了基本上会用到的),在看到这个方法里的内容

timeStamp = new Date()

getDateObj(val){
    this.timeStamp.setTime(val)
    return this.timeStamp
}

可以看到timeStamp实际上是一个时间对象,那么他就会具备这些方法,他会根据传入的时间戳参数,重新设定时间对象的值,然后返还时间对象,就可以使用对应时间戳的一些方法了,那么为什么不直接new Date()。

这样不就简单一点。//过渡句

这里就要首尾呼应一下了,这几天一直在学设计模式,看到一个享元模式,因为一个对象,他的属性,作用基本相同,并且我们需要new大量的这种对象,那既然它属性方法一致,是不是可以共享这个对象呢,借鉴了这个思想,我决定写下这个方法,如果这里不适用,欢迎大家拍醒我!

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值