JavaScript中两个日期之间的月份差异

224

在JavaScript中,如何计算两个Date()对象之间的差异,并仅返回差异的月数?

任何帮助都将是极好的 :)


一个月并不是非常准确的计量单位,因为月份的长度取决于哪个月。如果在一月和二月之间有一个持续30天的间隔,那么如果你按照31天的月份来考虑,那就不到1个月,但如果你考虑2月的28或29天,那就超过了1个月。 - Mark Byers
8
这个问题定义得不太清楚。2月28日23:58到3月1日00:01是一个月吗?还是只有一天?或者只有三分钟?或者三者都是? - Thilo
2
@Thilo 可能需要实现这个的人可能没有答案,因为他们的经理首先不知道他们在问什么。 :) - AlbertEngelB
我创建了一个小型库,用于计算两个日期之间的差,以年、月、日、小时等形式。也许有用? - KooiInc
29个回答

4

计算两个日期之间的差值,包括月份(天)的小数部分。


var difference = (date2.getDate() - date1.getDate()) / 30 +
    date2.getMonth() - date1.getMonth() +
    (12 * (date2.getFullYear() - date1.getFullYear()));

例如:
date1: 24/09/2015 (2015年9月24日)
date2: 09/11/2015 (2015年11月9日)
差异:2.5个月


我错过了什么……为什么这不是最好的选择?你可以选择如何四舍五入。如果你只想要完整的月份,可以使用Math.floor()函数。 - Todd Horst

4
有两种方法,一种是数学方法和快速方法,但取决于日历中的不确定性,或迭代和缓慢方法,但可以处理所有奇怪的情况(或至少委托给经过充分测试的库来处理)。
如果您通过日历进行迭代,将开始日期逐月增加,并查看是否超过结束日期。这将委托处理异常情况给内置的Date()类,但如果您要为大量日期执行此操作,则可能会慢。詹姆斯的答案采用了这种方法。尽管我不太喜欢这个想法,但我认为这是“最安全”的方法。如果您只进行一次计算,则性能差异确实可以忽略不计。我们倾向于过度优化只执行一次的任务。
现在,如果您正在计算数据集上的此函数,那么您可能不希望在每行(或者更糟糕地说,多次记录)上运行该函数。在这种情况下,您可以使用这里的任何其他答案,除了被接受的答案,它是错误的( new Date() new Date()之间的差异为-1)?
这是我对一个数学和快速方法的尝试,它考虑了不同的月份长度和闰年。如果您将应用此函数于数据集(反复执行此计算),则确实应该仅使用此类函数。如果您只需要执行一次,则使用上面的James迭代方法,因为您委托处理所有(很多)异常情况给Date()对象。
function diffInMonths(from, to){
    var months = to.getMonth() - from.getMonth() + (12 * (to.getFullYear() - from.getFullYear()));

    if(to.getDate() < from.getDate()){
        var newFrom = new Date(to.getFullYear(),to.getMonth(),from.getDate());
        if (to < newFrom  && to.getMonth() == newFrom.getMonth() && to.getYear() %4 != 0){
            months--;
        }
    }

    return months;
}

3

不考虑日期和时间的月份数量

在这种情况下,我不关心整个月、部分月、一个月有多长等问题。我只需要知道月份数量。一个相关的实际案例是每个月都要提交报告,我需要知道应该有多少份报告。

示例:

  • 一月 = 1 个月
  • 一月 - 二月 = 2 个月
  • 十一月 - 一月 = 3 个月

这是一个详细的代码示例,展示了数字的变化。

让我们拿两个时间戳作为例子,结果应该是4个月

  • 2019年11月13日的时间戳:1573621200000
  • 2020年2月20日的时间戳:1582261140000

可能会因您的时区/获取的时间而略有不同。日期、分钟和秒数并不重要,可以包含在时间戳中,但我们在实际计算中将忽略它们。

步骤1:将时间戳转换为JavaScript日期

let dateRangeStartConverted = new Date(1573621200000);
let dateRangeEndConverted = new Date(1582261140000);

步骤二:获取月份/年份的整数值

let startingMonth = dateRangeStartConverted.getMonth();
let startingYear = dateRangeStartConverted.getFullYear();
let endingMonth = dateRangeEndConverted.getMonth();
let endingYear = dateRangeEndConverted.getFullYear();

这个计算给我们

  • 起始月份:11
  • 起始年份:2019
  • 结束月份:2
  • 结束年份:2020

第三步:将(12 * (endYear - startYear)) + 1加到结束月份。

  • 这将使我们的起始月份保持在11
  • 这将使我们的结束月份等于15 2 + (12 * (2020-2019)) + 1 = 15

第四步:相减得到月数

15 - 11 = 4; 我们得到了4个月的结果。

29个月的例子

从2019年11月到2022年3月是29个月。如果你把它们放在Excel电子表格中,你会看到29行。

  • 我们的起始月份是11
  • 我们的结束月份是40 3 + (12 * (2022-2019)) + 1

40 - 11 = 29


2

这应该可以正常工作:

function monthDiff(d1, d2) {
    var months;
    months = (d2.getFullYear() - d1.getFullYear()) * 12;
    months += d2.getMonth() - d1.getMonth();
    return months;
}

2

这里提供了另一种更少循环的方法:

calculateTotalMonthsDifference = function(firstDate, secondDate) {
        var fm = firstDate.getMonth();
        var fy = firstDate.getFullYear();
        var sm = secondDate.getMonth();
        var sy = secondDate.getFullYear();
        var months = Math.abs(((fy - sy) * 12) + fm - sm);
        var firstBefore = firstDate > secondDate;
        firstDate.setFullYear(sy);
        firstDate.setMonth(sm);
        firstBefore ? firstDate < secondDate ? months-- : "" : secondDate < firstDate ? months-- : "";
        return months;
}

1
注意这个方法:当你仔细阅读时,它(显然)会改变传回给调用者的第一个日期(因为日期是按引用传递的)。 - Andiih

1
function calcualteMonthYr(){
    var fromDate =new Date($('#txtDurationFrom2').val()); //date picker (text fields)
    var toDate = new Date($('#txtDurationTo2').val());

var months=0;
        months = (toDate.getFullYear() - fromDate.getFullYear()) * 12;
        months -= fromDate.getMonth();
        months += toDate.getMonth();
            if (toDate.getDate() < fromDate.getDate()){
                months--;
            }
    $('#txtTimePeriod2').val(months);
}

1
function monthDiff(d1, d2) {
var months, d1day, d2day, d1new, d2new, diffdate,d2month,d2year,d1maxday,d2maxday;
months = (d2.getFullYear() - d1.getFullYear()) * 12;
months -= d1.getMonth() + 1;
months += d2.getMonth();
months = (months <= 0 ? 0 : months);
d1day = d1.getDate();
d2day = d2.getDate();
if(d1day > d2day)
{
    d2month = d2.getMonth();
    d2year = d2.getFullYear();
    d1new = new Date(d2year, d2month-1, d1day,0,0,0,0);
    var timeDiff = Math.abs(d2.getTime() - d1new.getTime());
          diffdate = Math.abs(Math.ceil(timeDiff / (1000 * 3600 * 24))); 
    d1new = new Date(d2year, d2month, 1,0,0,0,0);
    d1new.setDate(d1new.getDate()-1);
    d1maxday = d1new.getDate();
    months += diffdate / d1maxday;
}
else
{
      if(!(d1.getMonth() == d2.getMonth() && d1.getFullYear() == d2.getFullYear()))
    {
        months += 1;
    }
    diffdate = d2day - d1day + 1;
    d2month = d2.getMonth();
    d2year = d2.getFullYear();
    d2new = new Date(d2year, d2month + 1, 1, 0, 0, 0, 0);
    d2new.setDate(d2new.getDate()-1);
    d2maxday = d2new.getDate();
    months += diffdate / d2maxday;
}

return months;

}


1

以下代码返回两个日期之间的完整月份,同时考虑部分月份的天数。

var monthDiff = function(d1, d2) {
  if( d2 < d1 ) { 
    var dTmp = d2;
    d2 = d1;
    d1 = dTmp;
  }

  var months = (d2.getFullYear() - d1.getFullYear()) * 12;
  months -= d1.getMonth() + 1;
  months += d2.getMonth();

  if( d1.getDate() <= d2.getDate() ) months += 1;

  return months;
}

monthDiff(new Date(2015, 01, 20), new Date(2015, 02, 20))
> 1

monthDiff(new Date(2015, 01, 20), new Date(2015, 02, 19))
> 0

monthDiff(new Date(2015, 01, 20), new Date(2015, 01, 22))
> 0

1
这是我能找到的最简单的解决方案。它会直接返回月数。尽管如此,它总是给出一个绝对值。
new Date(new Date(d2) - new Date(d1)).getMonth();

对于非绝对值,您可以使用以下解决方案:

function diff_months(startDate, endDate) {
  let diff = new Date( new Date(endDate)  - new Date(startDate) ).getMonth();
  return endDate >= startDate ? diff : -diff;
}

2
非常好的解决方案。当差异超过一年时,请不要忘记使用类似 (diff.getFullYear() - 1970) * 12 的方式添加年份。 - Datz
1
当心这个看起来很简单的解决方案:当你执行 Date - Date 时,你得到一个整数秒数。将其封装在 new Date 中会给你一些日期,位于 1970 年 1 月或之后,然后你将从中获取序数月份。如果每个月有相等的天数,那么这样将有效,但事实并非如此。 - sam-w

1
function monthDiff(date1, date2, countDays) {

  countDays = (typeof countDays !== 'undefined') ?  countDays : false;

  if (!date1 || !date2) {
    return 0;
  }

  let bigDate = date1;
  let smallDate = date2;

  if (date1 < date2) {
    bigDate = date2;
    smallDate = date1;
  }

  let monthsCount = (bigDate.getFullYear() - smallDate.getFullYear()) * 12 + (bigDate.getMonth() - smallDate.getMonth());

  if (countDays && bigDate.getDate() < smallDate.getDate()) {
    --monthsCount;
  }

  return monthsCount;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接