日期计算是我们工作中经常见到的一个场景,过去我们是很难计算的,要算很多边界条件,比如 2 月有 28天,有的月份有 31 这些,好在有一个超级牛逼的库解决了关于日期的一切问题,他就是 moment momentjs.cn/
比如要实现类似 Twitter 上的一个功能, 计算发布的推文距离现在多长时间了,如下:
这不是单纯的计算两个时间的差值就可以了,你还要判断当前差值的时间范围,是分钟、小时、天、月还是年。
我们知道计算两个日期之间的差值,用 moment 是非常简单的,就是 diff 方法,如下使用:
moment().diff(moment('2022-02-20')); // 6747093242
他返回一个毫秒的数值,那就接下来就是怎么算他的时间范围了。
好在有一个方法可以解决这个问题,那就是 moment.duration() 他可以创建一个时长,参数是一个毫秒数值,于是就有了下面的代码:
export const timeDiff = (time) => {
const d = moment().diff(moment(time));
const dur = moment.duration(d);
if (dur.asMinutes() < 60) {
return `${dur.minutes()}分钟`;
}
if (dur.asHours() < 24) {
return `${dur.hours()}小时`;
}
if (dur.asDays() < 30) {
return `${dur.days()}天`;
}
if (dur.asMonths() < 12) {
return `${dur.months()}个月`;
}
return `${dur.years()}年`;
};
我们用 diff 方法获取到毫秒值,然后用 duration 方法获取时长对象,然后用 asDays 等的操作获取他用年计算的数值,asDays 和 days 的区别在于 asDays 是时长转化为天是多少,可以大于 30 天,可 days 是获取到时长中天的部分,不会大于 30 天。而且是有 asDays 是有小数点的,days 没有。
其实上面的代码还可以优化为:
export const timeDiff = (time) => {
const d = moment().diff(moment(time));
const dur = moment.duration(d);
if (dur.asYears() >= 1) {
return `${dur.years()}年`;
}
if (dur.asMonths() >= 1) {
return `${dur.months()}个月`;
}
if (dur.asDays() >= 1) {
return `${dur.days()}天`;
}
if (dur.asHours() >= 1) {
return `${dur.hours()}小时`;
}
return `${dur.minutes() || 1}分钟`;
};
这样是不是更清晰,从年开始算只要 >= 1 就可以了。最后如果小于 1 分钟都按 1 分钟计算。
但是上面的代码有一个问题,那就是 duration 的计算并不是按照自然月的,而是按照 30 天为一个月计算的,正常来说不会有问题,但是当遇到下面的场景就不行了。
const d = moment('2022-03-01').diff('2022-01-31');
const dur = moment.duration(d);
dur.asMonths() // 0
dur.asDays() // 29
1 月 31 日到 3 月 1日,我们字面上计算,也知道是差了一个整 2 月是吧,也就是相差一个月,但其实上述代码返回的却是 29 天,0 个月。这就不符合我们的直觉了。我们希望他能按照自然月来,因此就不能用 duration 这个方法了,因为他并不知道 2 月这回事。
我加以修改,代码变为:
moment('2022-03-01').diff('2022-01-31', 'months') // 1
我们启用 diff 的第二个参数,就是可以得到符合自然月规律的数值了。因此我们把上述方法重新写一下:
export const timeDiff = (time) => {
const years = moment().diff(time, 'years');
if (years > 0) {
return `${years}年`;
}
const months = moment().diff(time, 'months');
if (months > 0) {
return `${months}个月`;
}
const days = moment().diff(time, 'days');
if (days > 0) {
return `${days}天`;
}
const hours = moment().diff(time, 'hours');
if (hours > 0) {
return `${hours}小时`;
}
const minutes = moment().diff(time, 'minutes');
return `${minutes || 1}分钟`;
};
我们完全使用 diff 方法,就能搞定这个事情了。
如果有更好的方法,欢迎交流。