关于如何OA中如何计算请假时长

723 阅读2分钟

本篇文章是假设请假是半天起步,如果人性化的公司可以支持小时维度的请假,那更简单直接相减

考虑情况:

1、午休时间是否过滤,不过滤的话也很简单,直接结束和开始时间相减,需要过滤的话就要考虑分段处理了,开始当天(开始时间和当天下班时间差值),结束当天(当天上班时间和结束时间差值),中间的天数(上班时间和下班时间差值)。此处需要注意如何跳过午休时间

2、节假日,应为每年的节假日其实是不固定的,正常情况都是当年下半年假日办公布下一年的休假日(法定假日,涉及调休),所以针对公司来说 OA 系统中都是人工录入的。这里维护一个列表,在数组中存在就跳过

开始

这里用到dayjs 用于时间的计算

const dayjs = require('dayjs')
const customParseFormat = require('dayjs/plugin/customParseFormat')
dayjs.extend(customParseFormat)

// 当年假期const holidayList = ['2023-06-17', '2023-06-18']
// 是否过滤午休时间,默认是跳过,可自行修改
const rest = true
// 上下班时间
const startWorkTime = '09:00'
const endWorkTime = '18:00'
// 午休息时间
const startFreeTime = '12:00'
const endFreeTime = '13:30'

function getTotal(options) {  
// {      
// 请假开始时间  
//     beginAt,  
// 请假结束时间  
//     endAt,  
// }

// 这里做了开始和结束时间的调换,也可以直接 return 根据业务自行选择  
if (dayjs(options.beginAt).isAfter(dayjs(options.endAt))) 
    [options.beginAt, options.endAt] = [options.endAt, options.beginAt]  
const restList = holidayList.map(d => dayjs(d, 'YYYY-MM-DD'))  
let minute = 0  
const diffDay = dayjs(options.endAt)
                .startOf('d').diff(dayjs(options.beginAt).startOf('d'), 'd')  
for (let i = 0; i <= diffDay; i++) {    
    const day = dayjs(options.beginAt).add(i, 'd').startOf('d')    
    if (restList.some(d => d.isSame(day, 'd'))) continue    
    // 上午上班时间    
    const a = dayjs(`${day.format('YYYY-MM-DD')} ${startWorkTime}`,
                    'YYYY-MM-DD HH:mm')
    // 下午下班时间
    const f = dayjs(`${day.format('YYYY-MM-DD')} ${endWorkTime}`,
                    'YYYY-MM-DD HH:mm')
    // 上午午休开始时间    
    const ar = dayjs(`${day.format('YYYY-MM-DD')} ${startFreeTime}`,
                     'YYYY-MM-DD HH:mm')
    // 下午午休结束时间
    const fr = dayjs(`${day.format('YYYY-MM-DD')} ${endFreeTime}`,
                     'YYYY-MM-DD HH:mm')    
    
    let beginAt = dayjs(options.beginAt)    
    beginAt = beginAt.isBefore(a) ? a : beginAt.isAfter(f) ? f : beginAt    
    let endAt = dayjs(options.endAt)    
    endAt = endAt.isAfter(f) ? f : endAt    
    if (rest) {      
        beginAt = beginAt.isAfter(ar) && beginAt.isBefore(fr) ? fr : beginAt      
        endAt = endAt.isAfter(ar) && endAt.isBefore(fr) ? ar : endAt    
    }    
    let ranges = []    
    if (diffDay === 0) {      
        if (rest && beginAt.isBefore(ar) && endAt.isAfter(fr)) {        
            ranges = [[beginAt, ar], [fr, endAt]]      
        } else {        
            ranges = [[beginAt, endAt]]      
        }    
    } else if (i === 0) {      
        if (rest) {        
            ranges = beginAt.isBefore(ar) ? 
                        [[beginAt, ar], [fr, f]] : [[beginAt, f]]      
        } else {        
            ranges = [[beginAt, f]]      
        }    
    } else if (i === diffDay) {      
        if (rest) {        
            ranges = endAt.isBefore(ar) ? [[a, endAt]] : [[a, ar], [fr, endAt]]
        } else {        
            ranges = [[a, endAt]]      
        }    
    } else {      
        if (rest) {        
            ranges = [[a, ar], [fr, f]
        } else {        
            ranges = [[a, f]]      
        }    
    }    
    minute += ranges.map(range => {        
        const diff = range[1].diff(range[0], 'minute')        
        if (diff < 0) {          
            return 0        
        }        
        return diff      
        }).reduce((a, b) => a + b, 0)  }  
    
    const oh = parseFloat((minute / 60).toFixed(2))  
    const d =    oh % 8 > 0 ? 
                    oh % 8 < 4 ? 
                        Math.floor(oh / 8) + 0.5 
                        : Math.floor(oh / 8) + 1      
                    : oh / 8
// oh 为原始时长, d 是按照 >0 但是<0.50.5天算,>0.51天算,h是由天折算的请假时长  
    return { oh, d, h: d * 8 }
}