Разница в месяцах между двумя датами в JavaScript


135

Как бы я вычислил разницу для двух объектов Date () в JavaScript, при этом возвращая только количество месяцев в разнице?

Любая помощь будет отличной :)


Месяц не очень точная единица измерения, потому что длина месяца изменяется в зависимости от того, какой месяц это. Если интервал длится 30 дней между январем и февралем, это меньше 1 месяца, если вы думаете, с точки зрения 31-дневного месяца, но больше, чем 1 месяц, если вы считаете 28 или 29 дней февраля.
Марк Байерс

7
Не очень четко сформулированный вопрос. 28 февраля с 23:58 до 1 марта 00:01 один месяц? Или только один день? Или всего три минуты? Или все три?
Тило

1
@ Thilo У кого-то, кто нуждается в реализации, вероятно, не будет ответов, поскольку их менеджер не знает, о чем они спрашивают. :)
AlbertEngelB

Ответы:


229

Определение «количество месяцев в разнице» подлежит большой интерпретации. :-)

Вы можете получить год, месяц и день месяца из объекта даты 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 = января.)

Включение дробных месяцев в вышеприведенное намного сложнее, потому что три дня в типичном феврале - это большая доля этого месяца (~ 10,714%), чем три дня в августе (~ 9,677%), и, конечно, даже февраль является движущейся целью в зависимости от того, високосный ли это год.

Есть также некоторые библиотеки даты и времени, доступные для 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 = 0и December = 11.


11
Это самый короткий и простой для понимания код, который я когда-либо видел!
Омар

Ничего сложного в этом нет, за исключением того, что каждый начатый месяц считается. 31/03/2011 -> 01/05/2011 будет два месяца, а также 01/03/2011 -> 31/05/2011, но это должно быть три, если быть точным.
Натим

Если вы просто хотите узнать количество месяцев, это идеальный !!!!! Если вам нужно знать по дням месяца, это не сработает.
OSDM

1
Начните с того, что 2018/01 и 2017/01 годы разделены на 12 месяцев. Работайте задом наперед. Этот ответ получает это. ;)
Джерард ONeill

1
Спасибо! Эта функция очень полезна для меня
Trương Long

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.Если вы хотите указать количество месяцев между двумя датами, исключая месяц1 и месяц2

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;что делает то же самое, но более семантически правильно для меня
Натим

Да, хороший и более элегантный подход.
Микаил Абдуллаев

Это был лучший ответ. Спасибо!
Мариенке

Это просто и чисто. Спасибо за написание отличного кода.
нули и единицы

28

Вот функция, которая точно обеспечивает количество месяцев между двумя датами.
Поведение по умолчанию учитывает только целые месяцы, например, 3 месяца и 1 день приведет к разнице в 3 месяца. Вы можете предотвратить это, установив roundUpFractionalMonthsпараметр как 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
Если серьезно, кто проголосовал за это? Он прав, принятый ответ просто ... неправильный.
Майкл Блэкберн

2
Это самый подходящий ответ.
Калян Чавали

Это неправильно, если предположить, что date1 - 22 января, а date2 - 22 августа. Тогда он возвращает 7, что, очевидно, должно быть 8?
Николай Харбо

сделать этот принятый ответ. Это точнее, чем принято. Для примера принятый ответ не рассчитывает дни
Мункхельгер Туменбаяр

Принятый ответ Crowder с тех пор был обновлен. Между ним и этим ответом могут все еще быть важные различия, но на момент написания этой статьи проблемы, перечисленные здесь, были устранены.
клозах

23

Если вам нужно сосчитать полные месяцы, независимо от того, был ли месяц 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 января по 1 февраля (1 день).

Это будет охватывать следующее;

  • 1 января - 31 января ---> 30 дней ---> приведет к 0 (логично, так как это не полный месяц)
  • С 1 февраля по 1 марта ---> 28 или 29 дней ---> приведет к 1 (логично, так как это полный месяц)
  • С 15 февраля по 15 марта ---> 28 или 29 дней ---> будет 1 (логично с прошедшего месяца)
  • 31 января - 1 февраля ---> 1 день ---> приведет к 0 (очевидно, но упомянутый ответ в сообщении приводит к 1 месяцу)

2
Это то, о чем я думал, что пришел сюда для проверки, и это единственный ответ, который имеет для меня смысл
Андреас

2
Это не работает при сравнении двух месяцев, когда месяц короче. Например, с 31 марта по 30 апреля. Это весь апрель, поэтому ответ должен быть «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

Я знаю, что уже поздно, но в любом случае опубликовать его на тот случай, если это поможет другим. Вот функция, которую я придумал, которая, кажется, хорошо справляется с подсчетом различий в месяцах между двумя датами. По общему признанию, он намного более грубый, чем у мистера Краудера, но дает более точные результаты, проходя через объект даты. Это в 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 (). Для большинства (коротких) промежутков это даст тот же ответ, что и рассчитанные подходы, но когда вы имеете дело с длинными промежутками времени (включая високосные годы, високосные годы, которые не являются високосными годами, високосные годы). секунд и т. д.) вы, скорее всего, столкнетесь с исключением, когда вычитание месяцев и добавление лет будет неправильным. Смотрите мой ответ для исключений, когда расчет будет лучше.
Майкл Блэкберн

5

Чтобы расширить ответ @ 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 марта в 16:00 и 3 апреля в 15:00), но это более точно и всего пара строк кода.


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

Есть два подхода: математический и быстрый, но подверженный капризам в календаре, или итеративный и медленный, но он обрабатывает все странности (или, по крайней мере, делегаты обрабатывают их в хорошо проверенной библиотеке).

Если вы выполняете итерацию по календарю, увеличиваете дату начала на один месяц и смотрите, пропустим ли мы дату окончания. Это делегирует обработку аномалий встроенным классам Date (), но может быть медленным, если вы делаете это для большого числа дат. Джеймс ответ принимает такой подход. Как бы мне не нравилась идея, я думаю, что это «самый безопасный» подход, и если вы только делаете один расчет, разница в производительности действительно незначительна. Мы стремимся чрезмерно оптимизировать задачи, которые будут выполняться только один раз.

Теперь, если вы вычисляете эту функцию на наборе данных, вы, вероятно, не хотите запускать эту функцию в каждой строке (или, не дай бог, несколько раз для каждой записи). В этом случае вы можете использовать почти любые другие ответы здесь, кроме принятого ответа, который является просто неправильным (разница между new Date()иnew Date() равна -1)?

Вот мой пример математического и быстрого подхода, который учитывает разную продолжительность месяца и високосного года. Вы действительно должны использовать функцию, подобную этой, только если вы будете применять ее к набору данных (делать это снова и снова). Если вам просто нужно сделать это один раз, используйте итеративный подход Джеймса выше, поскольку вы делегируете обработку всех (многих) исключений для объекта 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

3

Вы также можете рассмотреть это решение, оно functionвозвращает разницу в месяце в целых числах или числах

Передача даты начала как первой или последней 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, чтобы учесть високосные годы
darryn.ten

2

Рассчитайте разницу между двумя датами с учетом доли месяца (дней).


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

Например:
дата1 : 24.09.2015 (24 сентября 2015 г.)
дата2 : 09.11.2015 (9 ноября 2015 г.)
разница: 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

Следующий код возвращает полные месяцы между двумя датами, учитывая также количество дней неполных месяцев.

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

ниже логика будет получать разницу в месяцах

(endDate.getFullYear()*12+endDate.getMonth())-(startDate.getFullYear()*12+startDate.getMonth())

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 дней?
Бен Талиадорос

0

Количество месяцев, когда день и время не имеют значения

В этом случае меня не интересуют полные месяцы, неполные месяцы, продолжительность месяца и т. Д. Мне просто нужно знать количество месяцев. Уместным примером из реальной жизни было бы то, когда отчет должен выходить каждый месяц, и мне нужно знать, сколько отчетов должно быть.

Пример:

  • Январь = 1 месяц
  • Январь - февраль = 2 месяца
  • Ноябрь - январь = 3 месяца

Это подробный пример кода, показывающий, куда идут цифры.

Давайте возьмем 2 отметки времени, которые должны получиться через 4 месяца

  • Отметка времени 13 ноября 2019 года: 1573621200000
  • Отметка времени 20 февраля 2020 года: 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 года по март 2022 года - 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

-7

Один из подходов - написать простую веб-службу Java (REST / JSON), использующую библиотеку JODA.

http://joda-time.sourceforge.net/faq.html#datediff

рассчитать разницу между двумя датами и вызвать эту услугу из JavaScript.

Это предполагает, что ваш бэкэнд находится на Java.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.