计算两个日期之间的时间差

1,557 阅读2分钟

日期计算是我们工作中经常见到的一个场景,过去我们是很难计算的,要算很多边界条件,比如 2 月有 28天,有的月份有 31 这些,好在有一个超级牛逼的库解决了关于日期的一切问题,他就是 moment momentjs.cn/

比如要实现类似 Twitter 上的一个功能, 计算发布的推文距离现在多长时间了,如下:

image.png

这不是单纯的计算两个时间的差值就可以了,你还要判断当前差值的时间范围,是分钟、小时、天、月还是年。

我们知道计算两个日期之间的差值,用 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 方法,就能搞定这个事情了。

如果有更好的方法,欢迎交流。