背景
项目中需要做一个倒计时的功能,可以根据选择的日期,算出距离今天的时间差。倒计时有多种模板,大致分为年月日、年月日时分秒以及单独的日。一开始也是在网上大致找了几种方法,但要么和项目要求不匹配,要么计算结果不准确,就自己写一个吧,本文记录分享一下。
实现
计算条件
年和月的差值计算是比较日期。举个例子:比如从2月1号0时0分0秒到3月1号0时0分0秒计为相差1个月,不管当年是闰年还是非闰年,即不管2月实际有多少天。不满1个月(计算时分秒)算0个月。年份同理。
两种方法
引用moment.js库实现
moment.js是时间处理常用的js库,这里使用了它的diff和subtract方法,大大简化了工作量。通过和原生方法对比就可看出。
/**
* 基于moment.js 实现的倒计时计算
* @param endTime {String,Date} - 倒计时结束时间
* @param maxUnit {String} - [maxUnit = "year"] 最大单位
* @param startTime {String,Date} - 倒计时开始时间,默认为当前时刻
* @return {Object} - 计算完成后返回的年月日时分秒数值
*/
function countDownTime(endTime, maxUnit = "year", startTime) {
let aUnitArr = ["year", "month", "day", "hour", "minute", "second"]
let iMaxIndex = aUnitArr.indexOf(maxUnit);
let end = moment(endTime);
let start = moment(startTime);
let result = {}
if (start - end > 0) {
throw new Error("开始时间不能晚于结束时间")
}
//过滤掉大于最大单位的单位
aUnitArr = aUnitArr.filter((item, index) => index >= iMaxIndex)
result[maxUnit] = end.diff(start, maxUnit);
if (aUnitArr.length > 1) {
aUnitArr.reduce((previous, current) => {
// 结束时间不断减去高位单位时间
end = end.subtract(result[previous], previous);
result[current] = end.diff(start, current);
return current
});
}
return result
};
原生js实现
function countDownTime2(endTime, maxUnit = "year", startTime) {
let end = new Date(endTime);
let start = startTime ? new Date(startTime) : new Date();
if (start - end > 0) {
throw new Error("开始时间不能晚于结束时间")
}
let aUnitArr = [
{
value: "second",
interval: 60,
secNum: 1 //该单位有多少秒,计算该单位最大差值用到
},
{
value: "minute",
interval: 60,
secNum: 60
},
{
value: "hour",
interval: 24,
secNum: 60 * 60
},
{
value: "day",
secNum: 60 * 60 * 24
},
{
value: "month",
interval: 12
},
{
value: "year",
},
]
let endList = getTimeList(end);
let startList = getTimeList(start);
const iMaxIndex = aUnitArr.findIndex(item => maxUnit === item.value);
// 当最大单位为日时分秒时过滤。月份最大单位需根据年份反算所以不能过滤掉年份
if (iMaxIndex > -1 && iMaxIndex < 4) {
aUnitArr = aUnitArr.filter((item, index) => index <= iMaxIndex);
}
let result = {};
aUnitArr.forEach((item, index) => {
if (index === iMaxIndex && iMaxIndex < 4) {
result[item.value] = Math.floor((end - start) / item.secNum / 1000);
return
}
if (endList[index] - startList[index] >= 0) {
result[item.value] = endList[index] - startList[index];
} else {
endList[index + 1]--;
result[item.value] = item.value === "day" ?
countDiffDays(start, startList[index], endList[index]) : endList[index] + item.interval - startList[index];
}
})
// 最大单位是月份时特殊处理
if (maxUnit === "month") {
result.month += result.year * 12
delete result.year
}
return result;
}
function getTimeList(t) {
return [t.getSeconds(), t.getMinutes(), t.getHours(), t.getDate(), t.getMonth() + 1, t.getFullYear()];
}
// 计算日期差值。开始时间本月剩余天数+结束时间当月日期数
function countDiffDays(time, startDay, endDay) {
let curDate = new Date(time);
let curMonth = curDate.getMonth();
/* 这里将时间设置为下个月之前,需要把日期设置小一点,否则极端情况,如果当天日期大于下一个月的总天数,月份会设置为下下个月 */
curDate.setDate(1)
curDate.setMonth(curMonth + 1);
curDate.setDate(0);//日期设置为前一个月的最后一天
let restDays = curDate.getDate() - startDay;
return restDays + endDay;
};
结语
原生js写得比较麻烦,暂时没有特别好的思路优化。如果大家有发现错误或者更好的思路,欢迎指正。