Skillnad i månader mellan två datum i JavaScript


Hur skulle jag räkna ut skillnaden mellan två Date()-objekt i JavaScript, medan jag endast returnerar antalet månader i skillnaden?

All hjälp skulle vara fantastiskt :)

Svar #1

Definitionen av "antalet månader i skillnaden" är föremål för mycket tolkning. :-)

Du kan få år, månad och dag i månaden från ett JavaScript datumobjekt. Beroende på vilken information du letar efter kan du använda dessa för att räkna ut hur många månader det är mellan två tidpunkter.

Till exempel, på stående fot:

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

(Observera att månadsvärden i JavaScript börjar med 0=januari.)

Att inkludera bråkdelar av månader i det ovanstående är mycket mer komplicerat, eftersom tre dagar i en typisk februari är en större del av den månaden (~10,714%) än tre dagar i augusti (~9,677%), och naturligtvis är även februari ett rörligt mål beroende på om det är ett skottår.

Det finns också några datum- och tidsbibliotek tillgängliga för JavaScript som förmodligen gör den här typen av saker lättare.

Notera: Det brukade finnas ett + 1 ovan, här:

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

Det beror på att jag ursprungligen sa:

...detta tar reda på hur många hela månader som ligger mellan två datum, utan att räkna delvisa månader (t.ex. exklusive månaden som varje datum ligger i).

Jag har tagit bort det av två skäl:

  1. Att inte räkna delvisa månader visar sig inte vara vad många (de flesta?) människor som kommer fram till svaret vill, så jag tänkte att jag borde separera dem.

  2. Det fungerade inte alltid ens enligt den definitionen. :-D (Förlåt.)

Svar #2

Om du inte tar hänsyn till dagens datum är detta utan tvekan den enklare lösningen

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

Var medveten om att månadsindex är 0-baserat. Detta betyder att Januari=0 och December=11.

Svar #3

Här är en funktion som exakt ger antalet månader mellan 2 datum.
Standardbeteendet räknar endast hela månader, t.ex. 3 månader och 1 dag kommer att resultera i en skillnad på 3 månader. Du kan förhindra detta genom att sätta parametern roundUpFractionalMonths till true, så en skillnad på 3 månader och 1 dag kommer att returneras som 4 månader.

Det accepterade svaret ovan (T.J. Crowders svar) är inte korrekt, det returnerar ibland fel värden.

Till exempel returnerar monthDiff(new Date('Jul 01, 2015'), new Date('Aug 05, 2015')) 0, vilket uppenbarligen är fel. Den korrekta skillnaden är antingen 1 hel månad eller 2 månader avrundat uppåt.

Här är funktionen jag skrev:

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

Svar #4

Ibland kanske du bara vill få mängden månader mellan två datum och helt ignorera dagdelen. Så till exempel, om du hade två datum - 2013/06/21 och 2013/10/18 - och du bara brydde dig om delarna 2013/06 och 2013/10, här är scenarierna och möjliga lösningar:

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. Om du bara vill ha antalet månader mellan de två datumen exklusive både månad1 och månad2

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

2. Om du vill inkludera någon av månaderna

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

3. Om du vill inkludera båda månaderna

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

Svar #5

Om du behöver räkna hela månader, oavsett om månaden har 28, 29, 30 eller 31 dagar. Nedan bör fungera.

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

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

Detta är en utökad version av svaret https://stackoverflow.com/a/4312956/1987208 men som åtgärdar fallet där det beräknar 1 månad för fallet från den 31 januari till den 1 februari (1 dag).

Detta kommer att täcka följande;

  • 1 januari till 31 januari ---> 30 dagar ---> kommer att resultera i 0 (logiskt eftersom det inte är en full månad)
  • 1 feb till 1 mar ---> 28 eller 29 dagar ---> kommer att resultera i 1 (logiskt eftersom det är en hel månad)
  • 15 februari till 15 mars ---> 28 eller 29 dagar ---> kommer resultera i 1 (logiskt eftersom en månad passerade)
  • 31 januari till 1 februari ---> 1 dag ---> kommer att resultera i 0 (uppenbart men det nämnda svaret i inlägget blir 1 månad)

Relaterade artiklar