造輪子 | 如何開發一個請假時長計算器?

leave-days-calculator的安裝和使用

安裝

$ npm install leave-days-calculator
複製代碼

引入

import leaveDaysCalculator form 'leave-days-calculator'
複製代碼

使用

const start = '2020-06-22 上午'
const end = '2020-06-24 下午'
let leaveDays = leaveDaysCalculator(start, end) // 3
複製代碼

時間的格式只支持: YYYY-MM-DD 上午/下午javascript

需求背景

自從作了小程序外勤打卡需求後,緊接着繼續作請假審批流程的開發,在處理請假表單的時候,有個請假時長的展現。當用戶選擇了請假的開始時間和結束時間後,能夠自動推算出請假時長,聽上去需求很常規。可是,哈哈哈,產品要求全部請假都按照半天請,好比說:java

請假的開始時間是6月1號上午,結束是6月2號上午,並非結束時間減去開始時間那麼簡單。上午到上午的這種狀況,多了半天吶。😂😂😂npm

那麼問題來了,如何準確的計算請假時長?小程序

解決方案

方案一:

按照當前請假,非當前請假兩類計算,數組

若是是當天請假:函數

  • 當天開始時間上午 - 當天結束時間上午,請假時長 0.5d;
  • 當天開始時間上午 - 當天結束時間下午,請假時長 1.0d;
  • 當天開始時間下午 - 當天結束時間上午,報錯警告處理;
  • 當天開始時間下午 - 當天結束時間下午,請假時長 0.5d;

若是是隔天計算: 結束時間 - 開始時間 = gap工具

  • 結束時間上午,開始時間上午,gap + 0.5
  • 結束時間上午,開始時間下午,gap + 1
  • 結束時間下午,開始時間上午,gap + 0
  • 結束時間下午,開始時間下午,gap + 0.5

此方案的核心是須要準確的計算出gap,可使用第三方工具來處理。從計算量上來看,工做量比較小。優化

方案二:

純手動計算,按照當天上下午,隔天上下午,隔月上下午,隔年上下午手動計算出請假時長。這個方案的開發時長比較長,在開發週期比較寬裕的狀況下我選擇了方案,由於我還很想知道具體是這麼計算的。給本身找點事幹,給枯燥的coding,增添點趣味。ui

手動實現一個請假時長計算器

基於開始時間和結束時間的格式都是 YYYY-MM-DD 上午/下午spa

圖一:流程圖

算起來有四種計算規則:

  • 同年同月同日請假時長計算
  • 同年同月非同日請假時長計算
  • 同年非同月請假時長計算
  • 非同年請假時長計算

1.jpg

須要一些基礎工具函數,好比說:

  • 是不是閏年
  • 某年的各個月份的天數
  • 去0函數
  • 加0函數

業務公共方法:

  • 開始時間,年月日
  • 結束時間,年月是
  • 同一天請假天數計算方法
  • 隔天請假天數計算方法
  • 開始月份總天數,開始月份請假天數計算方法 (跨月,年纔會用到的函數)
  • 結束月份請假天數計算方法(跨月,年纔會用到的函數)
  • 中間月份天數累計器
  • 開始年的請假天數計算: 開始月剩下的天數 + 剩下月份的天數
  • 結束年的請假天數計算: 結束月的天數 + 結束月以前月份的天數
  • 中間年份的天數累計器

如下是請假時長計算器的源碼,僅供參考。

/** * @Authors youxiaoxiao (gao0807@foxmail.com) * @Date: 2020-06-1 * @Last Modified by: youxiaoxiao * 請假時長計算器:(經過 (開始時間 + 結束時間)計算請假時長) */

/** * 是不是閏年 * @param {*} year */
function isLeapYear (year) {
    return year % 100 !== 0 && year % 4 === 0 || year % 400 === 0
}


/** * 獲取每一年每個月的天數 * @param {*} year * @param {*} month */
function getMaxDay (year, month) {
    year = parseFloat(year)
    month = parseFloat(month)
    if (month === 2) {
        return isLeapYear(year) ? 29 : 28
    }
    return [4, 6, 9, 11].indexOf(month) >= 0 ? 30 : 31
}


// 去0
function trimZero (val) {
    val = String(val)
    val = val ? parseFloat(val.replace(/^0+/g, '')) : ''
    val = val || 0
    val = val + ''
    return val
}

/** * 同一天請假 */
function sameDayLeave(startNoon, endNoon) {
    let leaveDays = 0
    if (startNoon === endNoon) {
        leaveDays = 0.5
    } 
    else if (startNoon === '上午' && endNoon === '下午') {
        leaveDays = 1
    } 
    else if (startNoon === '下午' && endNoon === '上午') {
        console.log('您選擇的時間有誤')
    }
    return leaveDays
}


/** * 隔天請假 */
function nextDayLeave(startDay, endDay, startNoon, endNoon) {
    let leaveDays = 0
    if (startNoon === '上午' && endNoon === '上午') {
        leaveDays = endDay - startDay + 0.5 
    } 
    else if (startNoon === '上午' && endNoon === '下午') {
        leaveDays = endDay - startDay + 1 
    } 
    else if (startNoon === '下午' && endNoon === '上午') {
        leaveDays = endDay - startDay
    } 
    else if (startNoon === '下午' && endNoon === '下午') {
        leaveDays = endDay - startDay + 0.5
    }
    return leaveDays
}


/** * 開始月份請假天數 跨月,年纔會用到的函數 */
function startMonthLeaveDays (startMonthAllDays, startDay, startNoon) {
    let startMonthLeave = 0
    if (startNoon === '上午') {
        startMonthLeave = Number(startMonthAllDays) -  Number(startDay) + 1
    } else if (startNoon === '下午') {
        startMonthLeave = Number(startMonthAllDays) -  Number(startDay) + 0.5
    }
    return Number(startMonthLeave)
}


/** * 結束月的請假天數 跨月,年纔會用到的函數 */
function endMonthLeaveDays(endDay, endNoon) {
    let endLeaveDays = 0
    if (endNoon === '上午') {
        endLeaveDays = endDay - 0.5
    } else if (endNoon === '下午') {
        endLeaveDays = endDay
    }
    return Number(endLeaveDays)
}


/** * 經過開始時間和結束時間,計算請假時長 * @param {*} start 【支持:格式 yyyy-mm-dd n】 * @param {*} end 【支持:格式 yyyy-mm-dd n】 */
export default (start, end) => {
    if(!start || !end) return 0

    const startArr = start.split(' ') // 開始時間
    const endArr = end.split(' ') // 結束時間
    const startDate = startArr[0] // 開始日期
    const endDate = endArr[0] // 結束日期


    const startDateArr = startDate.split('-') // 開始日期 數組化
    const endDateArr = endDate.split('-') // 結束日期 數組化


    const startYear = startDateArr[0] // 開始日期:年
    const startMonth = startDateArr[1] // 開始日期:月
    const startDay = startDateArr[2] // 開始日期:日
    const startNoon = startArr[1] // 開始時間的上下午


    const endYear = endDateArr[0] // 結束日期:年
    const endMonth = endDateArr[1] // 結束日期:月
    const endDay = endDateArr[2] // 結束日期:日
    const endNoon = endArr[1] // 結束時間的上下午


    // 開始月份的總天數
    const startMonthAllDays = getMaxDay(startYear, startMonth)


    /** * 中間月份的請假天數計算 */
    function centerMonthsLeave() {
        // 中間月份天數累加
        let leaveDays = 0
        let monthArr = [] // 月份數組
        for(let i = Number(trimZero(startMonth)); i <= Number(trimZero(endMonth)); i++) {
            monthArr.push(i)
        }

        let everyMonthDays = 0
        if (monthArr.length > 2) {
            monthArr.pop()
            monthArr.shift()
            
            monthArr.forEach(month => {
                everyMonthDays = Number(everyMonthDays) + Number(getMaxDay(startYear, month))
            })
        }
        return (Number(leaveDays) + Number(everyMonthDays)).toFixed(1)
    }


    /** * 開始月份和結束月份請假天數 相鄰月請假,或則跨月請假 */
    function startEndMonthNext() {
        // 開始月份請假天數計算
        let startLeaveDays = startMonthLeaveDays(startMonthAllDays, startDay, startNoon) 
        
        // 結束月份請假天數計算
        let endLeaveDays = endMonthLeaveDays(endDay, endNoon)

        // 天數累加
        return Number(startLeaveDays) + Number(endLeaveDays) || 0
    }


    /** * 開始年的請假天數計算 */
    function startYearLeaveDays () {
        // 開始月份天數計算
        let startMonthDays = startMonthLeaveDays(startMonthAllDays, startDay, startNoon) 

        // 剩下月份的天數
        let otherMonthDays = 0
        for(let i = Number(trimZero(startMonth)) + 1;  i <= 12; i++) {
            otherMonthDays = Number(otherMonthDays) + Number(getMaxDay(startYear, i))
        }

        return Number(startMonthDays) + Number(otherMonthDays) || 0
    }


    /** * 結束年的請假天數計算 */
    function endYearLeaveDays() {
        let endYearDays = 0
        // 結束月份計算
        let endLeaveDays = endMonthLeaveDays(endDay, endNoon)

        // 前幾個月份天數累加
        for(let i = 1; i < trimZero(endMonth); i++) {
            endYearDays = Number(endYearDays) + Number(getMaxDay(endYear, i))
        }

        return Number(endYearDays) + Number(endLeaveDays)
    }


    /** * 跨年,中間年假期計算 */
    function centerYearsLeaveDays () {
        let centerYear = 0
        if(endYear - startYear > 1) {
            let centerYears = []
            for(let startYear; startYear < endYear; startYear++) {
                centerYears.push(startYear)
            }
            centerYears.pop()
            centerYears.shift()
            centerYears.forEach(year => {
                for(let month = 1; month <=12; month++) {
                    centerYear = centerYear + getMaxDay(year, month)
                }
            })
        }
        return centerYear
    }

    // 請假天數
    let leaveAllDays = 0
    // 是否同年
    if (startYear === endYear) {
        // 是否同月
        if (startMonth === endMonth) {
            // 是否同天
            if (startDay === endDay) {
                leaveAllDays = sameDayLeave(startNoon, endNoon)

            // 同年 同月 隔天請假計算
            } else if (startDay < endDay) {
                leaveAllDays = nextDayLeave(startDay, endDay, startNoon, endNoon)

            } else {
                console.log('結束時間的天不對')
            }

        // 同年 不是同月
        } else if (startMonth < endMonth) {
            // 兩個月相鄰狀況, 開始月份剩餘天數,結束月份請假天數 之和
            let startEndMonthLeaveDays = startEndMonthNext()

            // 中間月份請假的天數
            let centerMonthsDays = centerMonthsLeave()

            // 請假天數彙總
            leaveAllDays = Number(startEndMonthLeaveDays) + Number(centerMonthsDays)
        } else {
            console.log('結束時間的月份不對')
        }

    // 不是同年
    } else if (startYear < endYear) {
        // 開始年的請假天數計算: 開始月剩下的天數 + 剩下月份的天數
        let startYearDays = startYearLeaveDays()

        // 結束年的請假天數計算: 結束月的天數 + 結束月以前月份的天數
        let endYearDays = endYearLeaveDays()

        // 中間年份的天數
        let centerYear = centerYearsLeaveDays()
        
        // 請假天數彙總累加
        leaveAllDays = Number(startYearDays) + Number(endYearDays) + Number(centerYear)
    } else {
        console.log('結束時間的年份不對')
    }

    // console.log(leaveAllDays)s
    return leaveAllDays
}
複製代碼

一個請假時長計算器就開發完了,其實方案一更省事一點,方案二的實現,還有不少能夠優化的點。最終封裝好的請假時長計算器 npm包:leave-days-calculator

若是你有其它的方案或者優化實現,歡迎在下方留言交流。

相關文章
相關標籤/搜索