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


135

如何计算JavaScript中两个Date()对象的差额,而只返回差额中的月数?

任何帮助将是巨大的:)


一个月不是一个非常准确的度量单位,因为一个月的长度根据它是哪个月而变化。如果间隔从一月到二月持续30天,那么如果以31天为一个月,则少于1个月;如果考虑2月的28或29天,则超过1个月。
Mark Byers 2010年

7
没有很好定义的问题。2月28日23:58到3月1日00:01是一个月吗?还是只有一天?还是只有三分钟?还是全部三个?
Thilo

1
@Thilo需要实施此操作的人可能没有答案,因为他们的经理首先都不知道他们在问什么。:)
AlbertEngelB 2015年

Answers:


228

“差异中的月数”的定义要经过很多解释。:-)

您可以从JavaScript日期对象获取年,月和日。根据您要查找的信息,您可以使用这些信息来计算两个时间点之间有多少个月。

例如,现成的:

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

(请注意,JavaScript中的月份值以0 =一月开头。)

在上面包含小数月份要复杂得多,因为典型的2月中的三天要占该月的一部分(〜10.714%)比8月的三天(〜9.677%)要大,当然,甚至2月也是一个移动的目标取决于是否是leap年。

还有一些可用于JavaScript的日期和时间库可能会使这种事情变得更容易。


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

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

那是因为最初我说:

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

我将其删除的原因有两个:

  1. 不算半个月,结果不是答案中想要多少(最多?)人,所以我认为我应该把他们分开。

  2. 即使按照该定义,它也不总是有效。:-D(对不起)


82

如果您不考虑月中的某天,那么这是迄今为止最简单的解决方案

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。这意味着January = 0December = 11


11
这是到目前为止我所见过的最短,最容易理解的代码!
2012年

除了计数每个月开始的事实之外,没有什么比这复杂的了。31/03/2011-> 01/05/2011和01/03/2011-> 31/05/2011是两个月,但确切地说应该是三个月。
Natim 2013年

如果您只想知道这个月数是完美的!!!!! 如果您需要根据当月的天数来了解,则将无法使用。
OSDM 2015年

1
从2018/01和2017/01相隔12个月这一事实开始。从那里向后工作。这个答案就可以了。;)
Gerard ONeill

1
谢谢!这个功能对我来说是非常有帮助
张庭长

29

有时您可能只想获取两个日期之间的月数,而完全忽略了日数部分。因此,例如,如果您有两个日期(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.如果只需要两个日期之间的月份数(不包括month1和month2)

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

2.如果要包括两个月中的任何一个

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

3.如果要同时包含两个月

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

好一个!对我来说完美。

3
对于最后一个,我将建议:numberOfMonths=(year2-year1)*12+(month2-month1)+1;这样做相同,但对我
而言

是的,不错,也更优雅。
Mikayil Abdullayev

这是最好的答案。谢谢!
marienke '17

这是简单而干净的。感谢您编写出色的代码。
零和一

28

这是一个功能,可准确提供两个日期之间的月数。
默认行为仅计算整个月,例如3个月和1天将导致3个月的差异。您可以通过将roundUpFractionalMonthsparam 设置为来防止这种情况true,因此3个月零1天的差异将作为4个月返回。

上面接受的答案(TJ 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

Crowder接受的答案此后已更新。它和这个答案之间可能仍然存在重要的区别,但是,在撰写本文时,这里列出的问题已得到解决。
clozach

22

如果您需要计算整个月,则无论是28、29、30或31天。下面应该工作。

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

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

这是答案https://stackoverflow.com/a/4312956/1987208的扩展版本,但修复了从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
Tha是我以为我来这里进行验证的,这对我来说是唯一有意义的答案
Andreas

2
比较两个月中较短的两个月时,这不起作用。例如,从3月31日到4月30日。这是所有的 4月,所以答案应该是“1”。
迈克尔·布莱克本

7

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)

在start_date和end_date之间的总月份:

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

6

我知道这确实很晚,但是无论如何都要发布它,以防它对其他人有所帮助。这是我想出的一个功能,似乎可以很好地计算两个日期之间月份的差异。诚然,它比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()。对于大多数(短)跨度,这将返回与计算得出的方法相同的答案,但是当您要处理较长的跨度(包括leap年,leap年,即不是le年,leap年,秒等),您很可能会遇到一个例外,即减去月份和增加年份是不正确的。请参阅我的答案以获取最佳计算时的例外情况。
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());

这将使您忽略两个日期之间的月份差。


4

要扩展@TJ的答案,如果您要查找简单的月份而不是完整的日历月份,则只需检查d2的日期是否大于或等于d1的日期即可。也就是说,如果d2在其月份中比d1在其月份中晚,则还有1个月。因此,您应该能够做到这一点:

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),但是它更准确,仅需几行代码。


4

有两种方法,数学的和快速的,但是受日历中的变化的影响,迭代的和缓慢的,但是处理所有的异常(或至少将它们处理到经过良好测试的库中)。

如果您遍历日历,则将开始日期增加一个月并查看我们是否超过了结束日期。这将异常处理委托给内置的Date()类,但是如果您要对大量日期执行此操作,则可能会很慢。詹姆斯的答案就是采用这种方法。尽管我不喜欢这个想法,但我认为这是“最安全”的方法,如果只进行一次计算,则性能差异实际上可以忽略不计。我们倾向于过度优化只能执行一次的任务。

现在,如果要在数据集上计算此函数,则可能不想在每一行上运行该函数(或者,禁止,每条记录多次)。在这种情况下,您几乎可以使用任何其他的答案在这里,除了公认的答案,这是错误的(之间的差异new Date(),并new Date()为-1)?

这是我采用数学快速方法的一次尝试,该方法说明了不同的月份长度和leap年。如果要将其应用于数据集(一遍又一遍地执行此计算),则实际上只应使用这样的函数。如果您只需要执行一次,请使用上面的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

在这里,您可以采用其他方法减少循环:

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 '16

3

您也可以考虑此解决方案,它function返回整数或数字的月份差

开始日期作为first或last 传递param是容错的。这意味着该函数仍将返回相同的值。

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);


您应该乘以365.25以考虑leap年
darryn.ten

2

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


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

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


2

这应该工作正常:

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

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

以下代码还通过考虑部分月份的nr天来返回两个日期之间的完整月份。

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
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
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;
}

0

看看我用什么:

function monthDiff() {
    var startdate = Date.parseExact($("#startingDate").val(), "dd/MM/yyyy");
    var enddate = Date.parseExact($("#endingDate").val(), "dd/MM/yyyy");
    var months = 0;
    while (startdate < enddate) {
        if (startdate.getMonth() === 1 && startdate.getDate() === 28) {
            months++;
            startdate.addMonths(1);
            startdate.addDays(2);
        } else {
            months++;
            startdate.addMonths(1);
        }
    }
    return months;
}

0

它还会计算天数并将其转换为月数。

function monthDiff(d1, d2) {
    var months;
    months = (d2.getFullYear() - d1.getFullYear()) * 12;   //calculates months between two years
    months -= d1.getMonth() + 1; 
    months += d2.getMonth();  //calculates number of complete months between two months
    day1 = 30-d1.getDate();  
    day2 = day1 + d2.getDate();
    months += parseInt(day2/30);  //calculates no of complete months lie between two dates
    return months <= 0 ? 0 : months;
}

monthDiff(
    new Date(2017, 8, 8), // Aug 8th, 2017    (d1)
    new Date(2017, 12, 12)  // Dec 12th, 2017   (d2)
);
//return value will be 4 months 

您能否添加有关其工作原理的更详细说明?这将使答案更好
serge1peshcoff

是否假设几个月有30天?
Ben Taliadoros

0

日期和时间无关紧要的月数

在这种情况下,我不必担心完整的月份,部分月份,一个月的时间等等。我只需要知道月份数即可。现实世界中的一个相关案例是每个月应提交一份报告,我需要知道应该提交多少份报告。

例:

  • 一月= 1个月
  • 1月-2月= 2个月
  • 11月-1月= 3个月

这是一个详细的代码示例,用于显示数字的去向。

让我们以2个时间戳记为例,这些时间戳记会在4个月内产生

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

可能与您的时区/时间有所不同。星期,分钟和秒无关紧要,可以将其包括在时间戳中,但是在实际计算中我们将忽略它。

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

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

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

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

这给了我们

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

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

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

步骤4:减去月份

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

29个月示例示例

2019年11月至2022年3月为29个月。如果将它们放入excel电子表格,则会看到29行。

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

40-11 = 29


-2
anyVar = (((DisplayTo.getFullYear() * 12) + DisplayTo.getMonth()) - ((DisplayFrom.getFullYear() * 12) + DisplayFrom.getMonth()));

3
尽管此代码段可能是解决方案,但包括说明确实有助于提高您的帖子质量。请记住,您将来会为读者回答这个问题,而这些人可能不知道您提出代码建议的原因。
尤金·波德斯克

发布答案取决于答案中未解释或定义的功能并没有太大用处。
RobG

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.