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个回答

322

“相隔月份数”的定义存在很多解释。:-)

您可以从JavaScript日期对象中获取年、月和日。根据您需要的信息,可以使用这些信息来计算两个时间点之间相差的月数。

例如,粗略地说:

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

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

function test(d1, d2) {
    var diff = monthDiff(d1, d2);
    console.log(
        d1.toISOString().substring(0, 10),
        "to",
        d2.toISOString().substring(0, 10),
        ":",
        diff
    );
}

test(
    new Date(2008, 10, 4), // November 4th, 2008
    new Date(2010, 2, 12)  // March 12th, 2010
);
// Result: 16

test(
    new Date(2010, 0, 1),  // January 1st, 2010
    new Date(2010, 2, 12)  // March 12th, 2010
);
// Result: 2

test(
    new Date(2010, 1, 1),  // February 1st, 2010
    new Date(2010, 2, 12)  // March 12th, 2010
);
// Result: 1

(JavaScript中的月份值以0 = 1月开始。)
以上包括小数月份要复杂得多,因为在典型的2月份中三天是该月的一个更大的分数(约10.714%),而在8月份中三天则是一个更小的分数(约9.677%),当然即使是2月也取决于它是否是闰年。
此外,JavaScript还有一些可用的日期和时间库,可能使这种事情变得更容易。

注意:上面曾经有一个+ 1,在这里:

months = (d2.getFullYear() - d1.getFullYear()) * 12;
months -= d1.getMonth() + 1;
// −−−−−−−−−−−−−−−−−−−−^^^^
months += d2.getMonth();

这是因为我原本说:

...这将查找两个日期之间有多少个“完整月份”,不包括部分月份(例如,不包括每个日期所在的月份)。

我删除它有两个原因:

  1. 不包括部分月份实际上并不是很多人(大多数人?)来到答案时想要的,所以我认为应该将它们分开。

  2. 即使按照这个定义,它也不能始终正常工作。:-D(抱歉。)


非常好的答案!谢谢!我解决了“上面曾经有+1”的问题,只需将一天添加到d2日期即可(对于我的情况起作用)。 - Renato Aloi
根据上下文,添加类似于 if (d2.getDate() >= d1.getDate()) months++; 这样的内容是否有意义呢?因为如果你想要获取两个日期之间的月份差异,那么你是在日期前面还是后面会有所不同。 - velop
1
@velop - 这完全取决于你对“时间差中的月数”的定义。我早就放弃了猜测人们似乎定义它的各种不同方式。但是OP从未这样做过。 - T.J. Crowder
你应该考虑使用 return Math.abs(months);我的意思是,它被命名为monthDiff,当结束日期作为第一个参数给出时,也应该执行相同的工作。 - Sercan Samet Savran

163
如果不考虑月份的日期,这绝对是比较简单的解决方案。

function monthDiff(dateFrom, dateTo) {
 return dateTo.getMonth() - dateFrom.getMonth() + 
   (12 * (dateTo.getFullYear() - dateFrom.getFullYear()))
}


//examples
console.log(monthDiff(new Date(2000, 01), new Date(2000, 02))) // 1
console.log(monthDiff(new Date(1999, 02), new Date(2000, 02))) // 12 full year
console.log(monthDiff(new Date(2009, 11), new Date(2010, 0))) // 1
请注意月份索引是从0开始的。这意味着一月 = 0十二月 = 11

15
这是我迄今为止看到的最短且最易于理解的代码! - Omar
这则内容与计算没有什么难度,唯一需要注意的是每个月份起始日都应被计算在内。例如31/03/2011 -> 01/05/2011这段时间间隔应该被视为两个月,而01/03/2011 -> 31/05/2011则应该被视为三个月,以保证准确性。 - Natim
如果你只想知道月份的数量,这个是完美的!!!如果你需要根据每个月的天数来计算,这个方法就不适用了。 - OSDM
1
从2018/01和2017/01相隔12个月这个事实开始,然后逆向推导。这个答案就是正确的。 ;) - Gerard ONeill
1
谢谢!这个函数对我非常有帮助。 - Trương Long
显示剩余2条评论

47
以下是一个函数,可以准确地提供两个日期之间的月份数。默认情况下,只计算整月差,例如3个月零1天将导致3个月的差异。您可以通过将roundUpFractionalMonths参数设置为true来防止这种情况,因此3个月零1天的差异将返回4个月。

上面接受的答案(T.J. Crowder的答案)不准确,有时会返回错误的值。

例如,monthDiff(new Date('Jul 01, 2015'), new Date('Aug 05, 2015')) 返回0 ,显然是错误的。正确的差异要么是1个完整的月份,要么是向上舍入的2个月。

以下是我编写的函数:
function getMonthsBetween(date1,date2,roundUpFractionalMonths)
{
    //Months will be calculated between start and end dates.
    //Make sure start date is less than end date.
    //But remember if the difference should be negative.
    var startDate=date1;
    var endDate=date2;
    var inverse=false;
    if(date1>date2)
    {
        startDate=date2;
        endDate=date1;
        inverse=true;
    }

    //Calculate the differences between the start and end dates
    var yearsDifference=endDate.getFullYear()-startDate.getFullYear();
    var monthsDifference=endDate.getMonth()-startDate.getMonth();
    var daysDifference=endDate.getDate()-startDate.getDate();

    var monthCorrection=0;
    //If roundUpFractionalMonths is true, check if an extra month needs to be added from rounding up.
    //The difference is done by ceiling (round up), e.g. 3 months and 1 day will be 4 months.
    if(roundUpFractionalMonths===true && daysDifference>0)
    {
        monthCorrection=1;
    }
    //If the day difference between the 2 months is negative, the last month is not a whole month.
    else if(roundUpFractionalMonths!==true && daysDifference<0)
    {
        monthCorrection=-1;
    }

    return (inverse?-1:1)*(yearsDifference*12+monthsDifference+monthCorrection);
};

6
说真的,是谁投了反对票?他说的没错,被接受的答案就是……错的。 - Michael Blackburn
2
这是最佳答案。 - Kalyan Chavali
如果说date1是1月22日,date2是8月22日,但程序返回7而不是正确答案8,那么就是有问题的。 - Nicolai Harbo
让这个回答被采纳。这个比被采纳的回答更准确。例如,被采纳的回答没有计算天数。 - Munkhdelger Tumenbayar
1
Crowder的被接受的答案已经更新。它和这个答案之间可能仍然存在重要差异,但截至本文撰写时,这里列出的问题已得到解决。 - clozach
显示剩余2条评论

34
有时候你可能只想获取两个日期之间的月份数量,而完全忽略掉天数部分。例如,如果你有两个日期- 2013/06/21 和 2013/10/18- 而你只关心它们的 2013/06 和 2013/10 部分,下面是可能出现的情况和解决方案:
var date1=new Date(2013,5,21);//Remember, months are 0 based in JS
var date2=new Date(2013,9,18);
var year1=date1.getFullYear();
var year2=date2.getFullYear();
var month1=date1.getMonth();
var month2=date2.getMonth();
if(month1===0){ //Have to take into account
  month1++;
  month2++;
}
var numberOfMonths; 

1.如果您只想知道两个日期之间的月数,但不包括月份1和月份2

numberOfMonths = (year2 - year1) * 12 + (month2 - month1) - 1;

2. 如果您想包含任何一个月份

numberOfMonths = (year2 - year1) * 12 + (month2 - month1);

3.如果您想同时包含这两个月份

numberOfMonths = (year2 - year1) * 12 + (month2 - month1) + 1;

不错啊!对我来说完美地运行了。 - user2541120
3
对于最后一个,我建议使用numberOfMonths=(year2-year1)*12+(month2-month1)+1;,它做的事情相同,但在语义上更加正确。 - Natim
是的,很好并且更优雅的方法。 - Mikayil Abdullayev
这是最好的答案。谢谢! - marienke
这很简单、干净。感谢您编写优秀的代码。 - zeros-and-ones

31

如果你需要计算完整的月份,而不管这个月是28、29、30还是31天。以下代码应该适用。

var months = to.getMonth() - from.getMonth() 
    + (12 * (to.getFullYear() - from.getFullYear()));

if(to.getDate() < from.getDate()){
    months--;
}
return months;

这是https://dev59.com/JXE85IYBdhLWcg3w8IXK#4312956答案的扩展版本,修复了从1月31日到2月1日(1天)计算1个月的情况。

它将涵盖以下内容:

  • 1月1日至1月31日 ---> 30天 ---> 结果为0(逻辑上正确,因为不是整月)
  • 2月1日至3月1日 ---> 28或29天 ---> 结果为1(逻辑上正确,因为是整个月)
  • 2月15日至3月15日 ---> 28或29天 ---> 结果为1(逻辑上正确,因为已过整个月)
  • 1月31日至2月1日 ---> 1天 ---> 结果为0(显然,但在帖子中提到的答案结果为1个月)

2
这就是我认为我来这里进行验证的原因,也是唯一有意义的答案。 - Andreas
2
当比较两个月份且其中一个月份天数较少时,此方法无法正常工作。例如,从3月31日到4月30日。这是整个四月,因此答案应该是“1”。 - Michael Blackburn

8
JavaScript中两个日期之间的月份差异:
 start_date = new Date(year, month, day); //Create start date object by passing appropiate argument
 end_date = new Date(new Date(year, month, day)

起始日期和结束日期之间的总月数:

 total_months = (end_date.getFullYear() - start_date.getFullYear())*12 + (end_date.getMonth() - start_date.getMonth())

8
你可以考虑这个解决方案,这个函数返回月份之间的整数或数字差异。
开始日期作为第一个或最后一个参数传递是容错的。这意味着,函数仍将返回相同的值。

const diffInMonths = (end, start) => {
   var timeDiff = Math.abs(end.getTime() - start.getTime());
   return Math.round(timeDiff / (2e3 * 3600 * 365.25));
}

const result = diffInMonths(new Date(2015, 3, 28), new Date(2010, 1, 25));

// shows month difference as integer/number
console.log(result);


1
你应该乘以365.25来考虑闰年。 - darryn.ten

6

我知道这有点晚了,但还是想发布一下,以防对其他人有所帮助。这是一个我想出来的函数,似乎很好地计算了两个日期之间的月份差异。它比Mr.Crowder的函数更为严谨,通过遍历日期对象提供了更准确的结果。它是用AS3编写的,但你只需要去掉强类型即可得到JS版本。欢迎任何人将其改进得更漂亮!

    function countMonths ( startDate:Date, endDate:Date ):int
    {
        var stepDate:Date = new Date;
        stepDate.time = startDate.time;
        var monthCount:int;

        while( stepDate.time <= endDate.time ) { 
            stepDate.month += 1;
            monthCount += 1;
        }           

        if ( stepDate != endDate ) { 
            monthCount -= 1;
        }

        return monthCount;
    }

这是大多数情况下最可能正确的方法。我们的日历中有很多例外和模糊不清之处,最好的方法是将它们委托给经过充分测试的库来处理,例如Date()。对于大多数(短)时间跨度,这将返回与计算方法相同的答案,但当您处理长时间跨度(包括闰年、非闰年的闰年、闰秒等)时,您更有可能遇到减去月份和加上年份会出现错误的异常情况。请参见我的答案,了解何时最好使用计算方法的异常情况。 - Michael Blackburn

5

将每个日期转化为月份计算,然后相减以求得差值。

var past_date = new Date('11/1/2014');
var current_date = new Date();

var difference = (current_date.getFullYear()*12 + current_date.getMonth()) - (past_date.getFullYear()*12 + past_date.getMonth());

这将为您获取两个日期之间的月份差异,忽略天数。


5

为了进一步解释@T.J.的答案,如果你只是想要简单的月份而不是完整的日历月份,那么你可以检查d2的日期是否大于或等于d1的日期。也就是说,如果d2在其月份比d1在其月份晚,那么就会多出一个月。所以你应该可以这样做:

function monthDiff(d1, d2) {
    var months;
    months = (d2.getFullYear() - d1.getFullYear()) * 12;
    months -= d1.getMonth() + 1;
    months += d2.getMonth();
    // edit: increment months if d2 comes later in its month than d1 in its month
    if (d2.getDate() >= d1.getDate())
        months++
    // end edit
    return months <= 0 ? 0 : months;
}

monthDiff(
    new Date(2008, 10, 4), // November 4th, 2008
    new Date(2010, 2, 12)  // March 12th, 2010
);
// Result: 16; 4 Nov – 4 Dec '08, 4 Dec '08 – 4 Dec '09, 4 Dec '09 – 4 March '10

这并没有完全解决时间问题(例如3月3日下午4:00和4月3日下午3:00),但它更加准确,且只需编写几行代码。


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