工时计算

196 阅读3分钟

js工时计算函数

  1. 实例是把工作时间、工作一整天工作工时、休息时间都写死的,感兴趣的话可以自己通过参数传进来,自行修改。
  2. 使用的工具库 dayjs
  3. 导入:import { workingHours } from “代码存放路径”
  4. 使用:workingHours('2024-10-01 00:00:00', '2024-10-07 23:59:59') 会返回总工时秒数,用一个参数接一下就ok了。

import dayjs from 'dayjs'


// 计算工时
// 定义工作时间段  [上午,下午,加班时间]
const workHours = [
    { start: '08:00', end: '11:20' },
    { start: '12:50', end: '17:30' },
    { start: '18:00', end: '20:30' }
]

// 工作一整天的工时 不加班 加班
const normalWorkTime = 28800
const overtimeWorkTime = 37800

// 定义休息时间(如果需要的话,可以添加)  
// 例如: [{ start: '10:00', end: '10:15' }]  
const breaks: any = [{ start: '10:00', end: '10:15' }]

/**
 * 
 * @param {string} start  开始时间
 * @param {string} end  结束时间
 * @param {boolean} type  是否有加班
 */
export const workingHours = (start: string, end: string, type: boolean = false) => {
    start = dayjs(start).format('YYYY-MM-DD HH:mm:ss')
    end = dayjs(end).format('YYYY-MM-DD HH:mm:ss')
    const startDate = start.split(' ')[0]
    const endDate = end.split(' ')[0]
    let dates = getDatesBetween(startDate, endDate)

    // 总工时
    let totalSeconds = 0;
    // 时间在同一天
    if (dates.length == 1) {
        // 遍历每个工作时间段  
        totalSeconds = computedSecond(start, end)
    }
    if (dates.length >= 2) {
        let baseTime = normalWorkTime
        const firstStartTime = start
        // 第一天结束时间
        let firstEndTime = start.split(' ')[0] + ' ' + workHours[1].end
        // 最后一天起始时间
        let afterStartTime = end.split(' ')[0] + ' ' + workHours[0].start
        const afterEndTime = end
        if (type) {
            baseTime = overtimeWorkTime
            firstEndTime = start.split(' ')[0] + ' ' + workHours[2].end
            afterStartTime = end.split(' ')[0] + ' ' + workHours[0].start
        }
        const s1 = computedSecond(dayjs(firstStartTime), dayjs(firstEndTime))
        const s2 = computedSecond(dayjs(afterStartTime), dayjs(afterEndTime))
        if (dates.length == 2) {
            totalSeconds = s1 + s2
        } else {
            const newDates = dates.slice(1, dates.length - 1)
            const sundayNubers = countSundays(newDates)
            // 计算休息时间
            let totalRestSeconds = 0;
            breaks.forEach(({ start, end }: any) => {
                const startDate = new Date();
                const parts = start.split(':');
                startDate.setHours(parseInt(parts[0]), parseInt(parts[1]), 0, 0);
                const endDate = new Date();
                const endParts = end.split(':');
                endDate.setHours(parseInt(endParts[0]), parseInt(endParts[1]), 0, 0);
                const diffInMs = endDate.getTime() - startDate.getTime();
                totalRestSeconds += diffInMs / 1000;
            });
            totalSeconds = s1 + s2 + (dates.length - 2 - sundayNubers) * (baseTime - totalRestSeconds)
        }
    }
    return totalSeconds > 0 ? totalSeconds : 0
}

// 计算日期数组
const getDatesBetween = (startDateStr: string, endDateStr: string) => {
    const startDate = new Date(startDateStr);
    const endDate = new Date(endDateStr);
    let dates: string[] = [];
    // 确保结束日期晚于或等于起始日期  
    if (endDate < startDate) return dates

    let currentDate = new Date(startDate);

    // 使用循环来添加日期,直到当前日期等于结束日期  
    while (currentDate <= endDate) {
        // 使用 padStart 方法来确保月份和日期都是两位数  
        let formattedDate = currentDate.getFullYear() + '-' +
            (currentDate.getMonth() + 1).toString().padStart(2, '0') + '-' +
            currentDate.getDate().toString().padStart(2, '0');
        dates.push(formattedDate);
        // 将日期增加一天  
        currentDate.setDate(currentDate.getDate() + 1);
    }

    // 返回日期数组  
    return dates;
}
// 计算秒
const computedSecond = (start: string | dayjs.Dayjs, end: string | dayjs.Dayjs) => {
    let total = 0
    const startDate = dayjs(start)
    const endDate = dayjs(end)
    for (const { start, end } of workHours) {
        const workStart = dayjs(startDate.format('YYYY-MM-DD') + 'T' + start);
        const workEnd = dayjs(startDate.format('YYYY-MM-DD') + 'T' + end);

        // 确保工作时间段在给定日期范围内  
        if (workStart.isAfter(endDate) || workEnd.isBefore(startDate)) {
            continue; // 跳过不在范围内的工作时间段  
        }

        // 截取与给定日期范围重叠的工作时间段  
        const overlapStart = workStart.isAfter(startDate) ? workStart : startDate;
        const overlapEnd = workEnd.isBefore(endDate) ? workEnd : endDate;

        // 计算重叠时间段的总秒数  
        let overlapDuration = overlapEnd.diff(overlapStart, 'second');

        // 减去重叠时间段内的休息时间  
        for (const { start: breakStart, end: breakEnd } of breaks) {
            const breakStartTime = dayjs(overlapStart.format('YYYY-MM-DD') + 'T' + breakStart);
            const breakEndTime = dayjs(overlapStart.format('YYYY-MM-DD') + 'T' + breakEnd);

            // 确保休息时间在重叠时间段内  
            if (breakStartTime.isAfter(overlapEnd) || breakEndTime.isBefore(overlapStart)) {
                continue; // 跳过不在重叠时间段内的休息时间  
            }

            // 截取与重叠时间段重叠的休息时间  
            const overlapBreakStart = breakStartTime.isAfter(overlapStart) ? breakStartTime : overlapStart;
            const overlapBreakEnd = breakEndTime.isBefore(overlapEnd) ? breakEndTime : overlapEnd;

            // 从重叠时间段中减去休息时间  
            overlapDuration -= overlapBreakEnd.diff(overlapBreakStart, 'second');
        }

        // 累加工作时间段(减去休息时间后)的总秒数  
        total += overlapDuration;
    }
    return total
}
// 计算周日
const countSundays = (dates: string[]) => {
    let sundayCount = 0;
    for (let i = 0; i < dates.length; i++) {
        const date = new Date(dates[i]);
        if (date.getDay() === 0) { // 星期天为0  
            sundayCount++;
        }
    }

    return sundayCount;
}