likes
comments
collection
share

Moment的diff方法两个日期正反比较值大小竟然不同?看完算法原理,原来是我天真了

作者站长头像
站长
· 阅读数 38

问题

大家好,我是数据里奥斯,今天有一段业务逻辑需要判断选择的时间范围不能超过3个月,这种常规的比较用moment.js的diff方法不是手到擒来么?

moment('2020-3-30').diff(moment('2020-7-01'), 'months')

console一下看了看结果:-3.03333333333333,不错,3个月出头,稳~就是这个负的有点难受,把开始结束时间换个位置吧

moment('2020-7-01').diff(moment('2020-3-30'), 'months')

卧槽,这console出来一看:3.064516129032258,额,这正负差别正常,为啥后面小数会有这么大差别?Moment傻逼了?不,一定是我傻逼了。。。。

思路

遇事不决,先看文档:diff() | Moment.js 文档 (momentjs.cn)

Emmm,好像没啥特别的,但是,有一行小字吸引了我:See more discussion on the month and year diffs here 看起来这里确实有Beef啊,就算不能解决,我也进去吐槽一下,哈哈

Moment的diff方法两个日期正反比较值大小竟然不同?看完算法原理,原来是我天真了

这个老哥,和我的问题不能说貌合神离,只能说一毛一样啊,原来也有和我一样的二傻子啊(???干嘛这样说我自己)我来看看下面各位大神是怎么教他做人的。。。

Moment的diff方法两个日期正反比较值大小竟然不同?看完算法原理,原来是我天真了

翻到最底下,这位吉布森老哥贴了个链接:difference should always return a "top-heavy balanced" duration with largest-first order of operations · Issue #993 · tc39/proposal-temporal (github.com)

这里面有一条评论是这样说的:

The algorithm Java uses seems to be something like this:

  1. Start with the smaller value: 2020-01-31
  2. Find the number of whole months that can be added to it without going past 2020-03-30. That's one month.
  3. Add one month to 2020-01-31 and constrain that result to a valid date, e.g. 2020-02-29
  4. Now find the number of whole days that can be added without going past the end. That's 30 days.
  5. Return P1M30D

看完这一段,我豁然开朗,拿我们今天遇到的实际case,我讲一下他解释的这段原理到底是怎么实现的:

  • diff算法是先加或者减每个整月一直到不能减,然后再看剩下的天数和当月比较的百分比
  • 比如07-01从零点开始计算,减3个月是04-01的零点,还剩下3-31、3-30 两天到3-30号零点,3月份有31天,所以是2/31=0.0645, 加在一起就是 3.064516129032258
  • 第二种情况,是一直加到03-30的零点开始算起,到06-30零点不能加整月了,这时离7-01零点还有1整天的时间,6月有30天,所以零头是1/30=0.333333333...,加在一起是 -3.03333333333333...

结论

所以,moment.js的diff方法在比较以天/月份/年份这样特殊粒度的单位时,都会优先按照整粒度扣除,剩下的小数部分,是根据子一级的粒度取当年/月/日为参照按比值算出的,这才有了这种A比B的值和B比A的值竟然不一样的情况。虽说一般来讲这个值多一点少一点不会有影响,毕竟我们是按找自己规定的粒度来比较的,但是这种原理能整明白,也不失为一种“学到了”的收获,嘿嘿

我是数据里奥斯~