date-util.js

/**
 * 日期相关函数。
 * @module date
 */

/**
 * @static
 * @description 时间格式化
 * @example
 * dateFormatter(new Date(2015, 1, 10, 14, 25, 33), 'yyyy-MM-dd hh:mm:ss')
 * dateFormatter(new Date(2015, 1, 10, 14, 25, 33), 'yyyy-M-d hh:mm:ss')
 * dateFormatter('2015-2-10 14:25:33', 'yyyy-M-d h:m:ss')
 * dateFormatter('2015-2-10 14:25:33', 'yyyy年第qq季度')
 * // 2015-02-10 14:25:33
 * // 2015-2-10 14:25:33
 * // 2015-2-10 14:25:33
 * // 2015年第01季度
 * @param {String|Date} date 需要格式化的时间。
 * @param {String} format 格式模板。
 * @return {String} 格式化后的时间字符。
 */

function dateFormatter (date, format) {
    // 时间格式化辅助函数 date:毫秒数 format:'yyyy-MM-dd hh:mm:ss'
    if (!date || date === '') {
        return ''
    }

    let cdate = date

    if (typeof cdate === 'string') {
        const mts = cdate.match(/(\/Date\((\d+)\)\/)/)

        if (mts && mts.length >= 3) {
            cdate = parseInt(mts[2])
        }
    }

    cdate = new Date(cdate)
    if (!cdate || cdate.toUTCString() === 'Invalid Date') {
        return ''
    }

    const map = {
        M: cdate.getMonth() + 1, // 月份
        d: cdate.getDate(), // 日
        h: cdate.getHours(), // 小时
        m: cdate.getMinutes(), // 分
        s: cdate.getSeconds(), // 秒
        q: Math.floor((cdate.getMonth() + 3) / 3), // 季度
        S: cdate.getMilliseconds() // 毫秒
    }

    const formatTime = format.replace(/([yMdhmsqS])+/g, function (all, t) {
        let v = map[t]

        if (v !== undefined) {
            if (all.length > 1) {
                v = '0' + v
                v = v.substr(v.length - 2)
            }
            return v
        }
        if (t === 'y') {
            return (cdate.getFullYear() + '').substr(4 - all.length)
        }
        return all
    })

    return formatTime
}

/**
 * @static
 * @description 判断是否是合法日期对象
 * @param {Date} date 日期对象
 * @returns {Boolean} true or false
 */

function isValidDate (date) {
    if (date instanceof Date === false) {
        return false
    }
    return date.toString() !== 'Invalid Date'
}

/**
 * @static
 * @description 判断是否是润年
 * @param {Number} year 年份
 * @returns {Boolean} true or false
 */

function isLeapYear (year) {
    return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0
}

/**
 * @static
 * @ignore
 * @description 判断是否是大月
 * @param {Number} month 月份
 * @returns {Boolean} true or false
 */

function isBigMonth (month) {
    const bigMonths = [0, 2, 4, 6, 7, 9, 11]

    return bigMonths.includes(month)
}

/**
 * @static
 * @description 获取月份天数
 * @param {Date} date 日期
 * @returns {Number} 天数
 */

function getMonthDays (date) {
    const month = date.getMonth()
    const year = date.getFullYear()

    if (isBigMonth(month)) {
        return 31
    }
    if (month === 1) {
        if (isLeapYear(year)) {
            return 29
        }
        return 28
    }
    return 30
}

/**
 * @static
 * @description 转换成日期对象
 * @param {String} time 时间字符串
 * @returns {Date} 日期对象
 */

function toDate (time) {
    let date = time

    if (typeof date === 'string') {
        date = new Date(date)
    }
    return date
}

/**
 * @static
 * @description 判断日期相等
 * @param {Date|String} stime 日期1
 * @param {Date|String} ttime 日期2
 * @returns {Boolean} true or false
 */

function isEqualDate (stime, ttime) {
    const sdate = toDate(stime)
    const tdate = toDate(ttime)

    if (!isValidDate(sdate) || !isValidDate(tdate)) {
        return false
    }
    return sdate.getTime() === tdate.getTime()
}

/**
 * @static
 * @ignore
 * @description 获取完全的日期,年月日时分秒毫秒
 * @param {Date|String} date 日期
 * @returns {Object} 包含年月日时分秒毫秒
 */
function getFullDate (date) {
    return {
        year: date.getFullYear(),
        month: date.getMonth(),
        date: date.getDate(),
        day: date.getDay(),
        hours: date.getHours(),
        minutes: date.getMinutes(),
        seconds: date.getSeconds(),
        milliSeconds: date.getMilliseconds()
    }
}

/**
 * @static
 * @description 判断日期模糊相等
 * @example
 * isEqualDateFuzzy('2015-3-2','2015-3-2 12:56','date')
 * // true
 * @param {Date|String} stime 日期1
 * @param {Date|String} ttime 日期2
 * @param {String} tag 相等界限,取值范围:<br>
 * year, month, date, hours, minutes, seconds, milliSeconds,<br>
 * day — 星期。
 * @returns {Boolean} true or false
 */

function isEqualDateFuzzy (stime, ttime, tag) {
    const sdate = toDate(stime)
    const tdate = toDate(ttime)

    if (!isValidDate(sdate) || !isValidDate(tdate)) {
        return false
    }

    const allTags = [
        'year',
        'month',
        'date',
        'hours',
        'minutes',
        'seconds',
        'milliSeconds'
    ]
    const index = allTags.indexOf(tag)

    if (index === -1) {
        return isEqualDate(sdate, tdate)
    }

    // 获取年月日星期时分秒毫秒组成的对象
    const fullsdate = getFullDate(sdate)
    const fulltdate = getFullDate(tdate)

    // 星期相等
    if (tag === 'day') {
        return fullsdate.day === fulltdate.day
    }

    for (let i = 0; i < index + 1; i++) {
        if (fullsdate[allTags[i]] !== fulltdate[allTags[i]]) {
            return false
        }
    }
    return true
}

/**
 * @static
 * @description 根据日、周、月、年获取指定日期所在时间段。
 * @example
 * getPeriod('2015-3-2','month')
 * // new Date('2015-3-1')
 * // new Date('2015-3-31')
 * @param {Date|String} date 日期
 * @param {Date|String} unit 单位:day, week, month, year
 * @returns {Array} 一个数组,包括开始日期和结束日期。
 */

function getPeriod (date = new Date(), unit) {
    const cdate = toDate(date)

    if (!isValidDate(cdate)) {
        return false
    }

    const year = cdate.getFullYear()
    const month = cdate.getMonth()
    const d = cdate.getDate()
    const day = cdate.getDay() || 7
    const oneDay = 24 * 60 * 60 * 1000
    const today = new Date(year, month, d)

    if (unit === 'day') {
        return [today, new Date(today.getTime() + oneDay)]
    }
    if (unit === 'week') {
        return [
            new Date(today.getTime() - (day - 1) * oneDay),
            new Date(today.getTime() + (8 - day) * oneDay)
        ]
    }
    if (unit === 'month') {
        const startDate = 1
        let nextMonth = ''

        if (month === 11) {
            nextMonth = new Date(year + 1, 0, 1)
        } else {
            nextMonth = new Date(year, month + 1, 1)
        }
        return [new Date(year, month, startDate), nextMonth]
    }
    if (unit === 'year') {
        return [new Date(year, 0, 1), new Date(year + 1, 0, 1)]
    }
}

/**
 * @static
 * @description 最大的连续天数数量。
 * @example
 * countMaxConsecutiveDate([
 *   new Date(2015, 1, 1),
 *   new Date(2015, 1, 4),
 *   new Date(2015, 1, 5)
 * ])
 * // 2
 * @param {Date} dates 日期数组
 * @returns {Number} 数量
 */

function countMaxConsecutiveDate (dates) {
    const cdates = dates.map(date => {
        const cdate = new Date(date)

        return new Date(
            cdate.getFullYear(),
            cdate.getMonth(),
            cdate.getDate()
        ).getTime()
    })
    const sortedDates = cdates.sort((a, b) => {
        return a - b
    })

    let count = 1
    let max = 1

    for (let i = 1; i < sortedDates.length; i++) {
        if (sortedDates[i] - sortedDates[i - 1] === 24 * 60 * 60 * 1000) {
            max++
        } else {
            if (max > count) {
                count = max
            }
            max = 1
        }
    }
    return count
}



export {
    dateFormatter,
    isValidDate,
    isLeapYear,
    isBigMonth,
    getMonthDays,
    toDate,
    isEqualDate,
    getFullDate,
    isEqualDateFuzzy,
    getPeriod,
    countMaxConsecutiveDate
}