我正在寻找向JavaScript日期添加X个月的最简单,最干净的方法。

我不想处理一年的时间,也不必编写自己的函数。

是否有内置功能可以做到这一点?

评论

尝试在日期的原型对象中添加一个方法,例如:-------------- Date.prototype.addMonth = function(n){return new Date(this.setMonth(this.getMonth( )+ n)); };

@ kr37,如果目标月份没有今天的天数,Moises Hidalgo的答案将无法正常工作。 bmpasini的答案也可以解决这个问题

这里的答案不是很好,更好的答案是在JavaScript中向日期添加月份。

#1 楼

以下函数在JavaScript(源)中为日期添加月份。它考虑了年度结转和不同的月份长度:




 function addMonths(date, months) {
    var d = date.getDate();
    date.setMonth(date.getMonth() + +months);
    if (date.getDate() != d) {
      date.setDate(0);
    }
    return date;
}

// Add 12 months to 29 Feb 2016 -> 28 Feb 2017
console.log(addMonths(new Date(2016,1,29),12).toString());

// Subtract 1 month from 1 Jan 2017 -> 1 Dec 2016
console.log(addMonths(new Date(2017,0,1),-1).toString());

// Subtract 2 months from 31 Jan 2017 -> 30 Nov 2016
console.log(addMonths(new Date(2017,0,31),-2).toString());

// Add 2 months to 31 Dec 2016 -> 28 Feb 2017
console.log(addMonths(new Date(2016,11,31),2).toString()); 








上面的解决方案涵盖了一个极端的情况,即从一个月开始移动的天数比目标月份大。例如。


在2020年2月29日之前应增加12个月(应为2021年2月28日)
在2020年8月31日之前应增加1个月(应为2020年9月30日)

如果应用setMonth时某月的某天发生了变化,那么我们知道由于月长的不同,我们已经溢出到了下个月。在这种情况下,我们使用setDate(0)移回上个月的最后一天。

注意:此答案的此版本替换了不能正常处理不同月份长度的早期版本(以下)。

var x = 12; //or whatever offset
var CurrentDate = new Date();
console.log("Current date:", CurrentDate);
CurrentDate.setMonth(CurrentDate.getMonth() + x);
console.log("Date after " + x + " months:", CurrentDate);


评论


小心-这不适用于极端情况,例如大多数月份的第31天。例如,使用Javascript的标准Date对象的2011年10月31日+ 1个月就是使用此方法。

– TAD
2011年9月21日在16:37

不错,老兄。我没有看到那个,我感到很惊讶。请注意,T-SQL选择DATEADD(月,1,“ 2011-10-31”)会产生“ 2011-11-30”,这对我来说是合理的。我得了10票,但答案很差。凉。 :-)

–乍得
2011-11-26 3:53



还要注意leap年-在a年中将1年添加到2月29日将为您带来不确定的结果,具体取决于您使用的语言(作为一般答案)。这就是在2012年使Microsoft Azure云平台瘫痪了几个小时的原因

–本·瓦尔丁
2012年4月8日在21:50

这里有一个addMonths函数,该函数尊重月底(例如2012-01-31 + 1个月= 2012-02-29,依此类推)。

–RobG
2012年10月9日下午5:29

只需添加设置每个月的第1个日期:today.setHours(0,0,0,0); today.setDate(1);

–阿列克谢·斯特拉克(Alexey Strakh)
2014年4月4日在20:28

#2 楼

我正在使用moment.js库进行日期时间操作。
示例代码添加一个月:

var startDate = new Date(...);
var endDateMoment = moment(startDate); // moment(...) can also be used to parse dates in string format
endDateMoment.add(1, 'months');


评论


为了您自己的理智,请使用moment.js。

–加ʀʀʏ
2015年11月6日,19:01

绝对同意@garry:使用moment.js。

–米歇尔
16年1月6日在12:07

我同意这是最干净的方法,但是问题是“是否有内置的功能可以做到这一点?”瞬间使页面大小增加了数十Kb

–Evgeny
19年1月31日在20:58

很好我在网上找不到其他插件能比这做得更好。

– Eraniichan
19年8月14日在0:04

返回moment(datevar).subtract(1,'days'); //解决了我的生活!

–FBaez51
7月20日18:48

#3 楼

此函数处理边缘情况并快速:

function addMonthsUTC (date, count) {
  if (date && count) {
    var m, d = (date = new Date(+date)).getUTCDate()

    date.setUTCMonth(date.getUTCMonth() + count, 1)
    m = date.getUTCMonth()
    date.setUTCDate(d)
    if (date.getUTCMonth() !== m) date.setUTCDate(0)
  }
  return date
}


测试:

> d = new Date('2016-01-31T00:00:00Z');
Sat Jan 30 2016 18:00:00 GMT-0600 (CST)
> d = addMonthsUTC(d, 1);
Sun Feb 28 2016 18:00:00 GMT-0600 (CST)
> d = addMonthsUTC(d, 1);
Mon Mar 28 2016 18:00:00 GMT-0600 (CST)
> d.toISOString()
"2016-03-29T00:00:00.000Z"


非UTC的更新日期:(由A.Hatchkins编写)

function addMonths (date, count) {
  if (date && count) {
    var m, d = (date = new Date(+date)).getDate()

    date.setMonth(date.getMonth() + count, 1)
    m = date.getMonth()
    date.setDate(d)
    if (date.getMonth() !== m) date.setDate(0)
  }
  return date
}


测试:

> d = new Date(2016,0,31);
Sun Jan 31 2016 00:00:00 GMT-0600 (CST)
> d = addMonths(d, 1);
Mon Feb 29 2016 00:00:00 GMT-0600 (CST)
> d = addMonths(d, 1);
Tue Mar 29 2016 00:00:00 GMT-0600 (CST)
> d.toISOString()
"2016-03-29T06:00:00.000Z"


评论


这里唯一合理的答案。但是必须注意非UTC日期。它可能带来意想不到的结果(例如,UTC + 1时区午夜1月31日+ 1个月= 3月1日)

– Antony Hatchkins
16-2-15在18:47



另一个问题是它既修改了原始日期又返回了修改后的值。也许将其添加到日期原型中或将局部变量用于函数中更改后的日期是否有意义?

– Antony Hatchkins
16年2月15日在19:01

谢谢安东尼,一个局部变量才有意义。

– aMarCruz
16年2月19日在5:04

我建议您删除“返回日期”行或添加本地变量。

– Antony Hatchkins
16年2月19日在16:37

使用局部变量和单独的测试进行了更新。

– aMarCruz
16年7月16日在17:22

#4 楼

考虑到这些答案都不能解释当月变化时的当前年份,因此您可以在下面找到一个我应该处理的答案:

方法:

Date.prototype.addMonths = function (m) {
    var d = new Date(this);
    var years = Math.floor(m / 12);
    var months = m - (years * 12);
    if (years) d.setFullYear(d.getFullYear() + years);
    if (months) d.setMonth(d.getMonth() + months);
    return d;
}


用法:

return new Date().addMonths(2);


评论


如果结果月份中没有对应的日期(例如1月31日+ 1个月=> 2月3日或3月3日),则此答案不考虑增加月份。似乎还认为向Date添加超过12个月存在问题:没有。例如,添加13个月时无需添加1年零1个月。只需添加13个月。

–RobG
3月4日下午6:27

#5 楼

取自@bmpsini和@Jazaret响应,但不扩展原型:使用普通函数(为什么扩展本机对象是一种不好的做法?):

function isLeapYear(year) { 
    return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0)); 
}

function getDaysInMonth(year, month) {
    return [31, (isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
}

function addMonths(date, value) {
    var d = new Date(date),
        n = date.getDate();
    d.setDate(1);
    d.setMonth(d.getMonth() + value);
    d.setDate(Math.min(n, getDaysInMonth(d.getFullYear(), d.getMonth())));
    return d;
}


使用它:

var nextMonth = addMonths(new Date(), 1);


#6 楼

从上面的答案中,唯一一个处理极端情况的问题(来自datejs库的bmpasini)存在一个问题: />
var date = new Date("03/31/2015");
var newDate = date.addMonths(1);
console.log(newDate);
// VM223:4 Thu Apr 30 2015 00:00:00 GMT+0200 (CEST)


更糟:

newDate.toISOString()
//"2015-04-29T22:00:00.000Z"


这是由于未设置时间,因此恢复为00:00:00,然后可能会由于时区或节省时间的更改或其他原因而跳至前一天。

这是我提出的解决方案,它没有这个问题,而且我认为这样做更优雅它不依赖于硬编码的值。

var date = new Date("01/01/2015");
var newDate = date.addMonths(3);
console.log(newDate);
//VM208:4 Wed Apr 01 2015 00:00:00 GMT+0200 (CEST)
newDate.toISOString()
//"2015-03-31T22:00:00.000Z"


单元测试成功,并具有以下功能:

/**
* @param isoDate {string} in ISO 8601 format e.g. 2015-12-31
* @param numberMonths {number} e.g. 1, 2, 3...
* @returns {string} in ISO 8601 format e.g. 2015-12-31
*/
function addMonths (isoDate, numberMonths) {
    var dateObject = new Date(isoDate),
        day = dateObject.getDate(); // returns day of the month number

    // avoid date calculation errors
    dateObject.setHours(20);

    // add months and set date to last day of the correct month
    dateObject.setMonth(dateObject.getMonth() + numberMonths + 1, 0);

    // set day number to min of either the original one or last day of month
    dateObject.setDate(Math.min(day, dateObject.getDate()));

    return dateObject.toISOString().split('T')[0];
};


#7 楼

简单的解决方案:2678400000是31天(以毫秒为单位)

var oneMonthFromNow = new Date((+new Date) + 2678400000);


更新:

使用此数据构建我们自己的函数:



2678400000-31天

2592000000-30天

2505600000-29天

2419200000-28天


评论


有些月份没有31天

– Alexandru Severin
16年1月27日在13:44

同意,但是您可以轻松调整或编写功能

– dimitru博士
16年1月27日在20:08

我猜想人们正在寻找的功能就是自动考虑数月之内的功能。

–亚历山大·伯德(Alexander Bird)
16年6月6日在2:38

然后@AlexanderBird使用其他人创建的防弹工具,例如moment.js

– dimitru博士
16年6月7日在21:35

此答案不能满足夏令时的要求,因为并非整天都长24小时(或8.64e7毫秒)。

–RobG
17年1月11日在20:20

#8 楼

正如大多数答案所强调的那样,我们可以将setMonth()方法与getMonth()方法一起使用,以将给定日期添加特定的月份数。

示例:(如@ChadD在其答案中提到的那样) 。)


var x = 12; //or whatever offset 
var CurrentDate = new Date();
CurrentDate.setMonth(CurrentDate.getMonth() + x);



但是我们应该谨慎使用此解决方案,因为这样会遇到边缘情况的麻烦。

要处理边缘情况,以下链接中给出的答案会有所帮助。 https://stackoverflow.com/a/13633692/3668866

#9 楼

d = new Date();

alert(d.getMonth()+1);


月份的索引是从0开始的,因此它应该发出alert(4),即5(可能);

评论


此方法可能返回值<0且大于12

–帕特里克(Patrick)
16/09/15 '16:34

#10 楼

只是添加接受的答案和评论。

var x = 12; //or whatever offset
var CurrentDate = new Date();

//For the very rare cases like the end of a month
//eg. May 30th - 3 months will give you March instead of February
var date = CurrentDate.getDate();
CurrentDate.setDate(1);
CurrentDate.setMonth(CurrentDate.getMonth()+X);
CurrentDate.setDate(date);


评论


当您将Date(31)设置为“ 2 Feb”时,您将获得“ 3 Mar”。这是您真正想要的吗?

– Antony Hatchkins
16年2月15日在18:38

#11 楼

我写了这个替代解决方案,对我来说很好。当您希望计算合同结束时,此功能很有用。例如,开始= 2016-01-15,月份= 6,结束= 2016-7-14(即最后一天-1):

<script>
function daysInMonth(year, month)
{
    return new Date(year, month + 1, 0).getDate();
}

function addMonths(date, months)
{
    var target_month = date.getMonth() + months;
    var year = date.getFullYear() + parseInt(target_month / 12);
    var month = target_month % 12;
    var day = date.getDate();
    var last_day = daysInMonth(year, month);
    if (day > last_day)
    {
        day = last_day;
    }
    var new_date = new Date(year, month, day);
    return new_date;
}

var endDate = addMonths(startDate, months);
</script>


示例:

addMonths(new Date("2016-01-01"), 1); // 2016-01-31
addMonths(new Date("2016-01-01"), 2); // 2016-02-29 (2016 is a leap year)
addMonths(new Date("2016-01-01"), 13); // 2017-01-31
addMonths(new Date("2016-01-01"), 14); // 2017-02-28


#12 楼

以下是如何通过表单字段基于日期输入(membershipssignup_date)+添加的月份(membershipsmonths)计算未来日期的示例。

membershipsmonths字段的默认值为0

触发器链接(可以是隶属条款字段上的onchange事件):

<a href="#" onclick="calculateMshipExp()"; return false;">Calculate Expiry Date</a>

function calculateMshipExp() {

var calcval = null;

var start_date = document.getElementById("membershipssignup_date").value;
var term = document.getElementById("membershipsmonths").value;  // Is text value

var set_start = start_date.split('/');  

var day = set_start[0];  
var month = (set_start[1] - 1);  // January is 0 so August (8th month) is 7
var year = set_start[2];
var datetime = new Date(year, month, day);
var newmonth = (month + parseInt(term));  // Must convert term to integer
var newdate = datetime.setMonth(newmonth);

newdate = new Date(newdate);
//alert(newdate);

day = newdate.getDate();
month = newdate.getMonth() + 1;
year = newdate.getFullYear();

// This is British date format. See below for US.
calcval = (((day <= 9) ? "0" + day : day) + "/" + ((month <= 9) ? "0" + month : month) + "/" + year);

// mm/dd/yyyy
calcval = (((month <= 9) ? "0" + month : month) + "/" + ((day <= 9) ? "0" + day : day) + "/" + year);

// Displays the new date in a <span id="memexp">[Date]</span> // Note: Must contain a value to replace eg. [Date]
document.getElementById("memexp").firstChild.data = calcval;

// Stores the new date in a <input type="hidden" id="membershipsexpiry_date" value="" name="membershipsexpiry_date"> for submission to database table
document.getElementById("membershipsexpiry_date").value = calcval;
}


#13 楼

正如所呈现的许多复杂,丑陋的答案所表明的那样,日期和时间对于使用任何语言的程序员而言都是一场噩梦。我的方法是将日期和'delta t'值转换为Epoch Time(以毫秒为单位),执行任何算术,然后转换回“人工时间”。这是为稍有不同的用例编写的,但是您应该能够轻松地将其用于相关任务。

编辑:此处的完整源代码!

评论


没用的答案,因为它不能处理不同天数的月份,这是个问题。

– Benubird
15年1月13日在8:21

@Benubird-自从您礼貌地问了问题,我已经上载了完整的源代码。在这篇文章中链接。

–丹·阿奎斯特(Dan Ahlquist)
2015年1月15日,下午2:06

这也不适合夏令时,因为并非整天都是24小时(或8.64e7毫秒)长。

–RobG
17年1月11日在20:17

不,不是。 DST是本地化的问题。

–丹·阿奎斯特(Dan Ahlquist)
17年6月20日在14:42

#14 楼

所有这些似乎都太复杂了,我想它引起了关于“一个月”到底意味着什么的争论。是30天吗?从第一天到第一天是什么意思?从最后一天到最后一天?

如果是后者,那么在2月27日添加一个月可以使您到达3月27日,但是在2月28日添加一个月可以使您到达3月31日(跳距除外)年,到3月28日为止。然后从3月30日减去一个月会得到... 2月27日?谁知道...

对于那些寻求简单解决方案的人,只需添加毫秒即可完成。

function getDatePlusDays(dt, days) {
  return new Date(dt.getTime() + (days * 86400000));
}




Date.prototype.addDays = function(days) {
  this = new Date(this.getTime() + (days * 86400000));
};


评论


我感觉很明显,“增加X个月”意味着月份增加X。确实,您的答案的编码算法步骤更少,但不幸的是,我认为准确性胜过简单。当人们寻找答案时,他们希望“ 2016年7月31日”成为“ 2016年8月31日”。这不会那样做。此外,如果您真的希望以准确性为代价来简化操作,为什么不坚持使用d.setMonth(d.getMonth()+ 1)?因此,这甚至不是最简单的想法。

–亚历山大·伯德(Alexander Bird)
16年6月6日在2:36

这也不适合夏令时,因为并非整天都是24小时(或8.64e7毫秒)长。

–RobG
17年1月11日在20:19

#15 楼

这适用于所有边缘情况。 newMonth的怪异计算处理的是负月份输入。如果新的月份与预期的月份不匹配(例如2月31日),则会将月份的日期设置为0,这表示“上个月末”:
function dateAddCalendarMonths(date, months) {
    monthSum = date.getMonth() + months;
    newMonth = (12 + (monthSum % 12)) % 12;
    newYear = date.getFullYear() + Math.floor(monthSum / 12);
    newDate = new Date(newYear, newMonth, date.getDate());
    return (newDate.getMonth() != newMonth)
        ? new Date(newDate.setDate(0))
        : newDate;
}


#16 楼

我更改了接受的答案以保持原始日期不变,因为我认为应该在这样的函数中使用。



 function addMonths(date, months) {
    let newDate = new Date(date);
    var day = newDate.getDate();
    newDate.setMonth(newDate.getMonth() + +months);
    if (newDate.getDate() != day)
        newDate.setDate(0);
    return newDate;
} 




#17 楼

addDateMonate : function( pDatum, pAnzahlMonate )
{
    if ( pDatum === undefined )
    {
        return undefined;
    }

    if ( pAnzahlMonate === undefined )
    {
        return pDatum;
    }

    var vv = new Date();

    var jahr = pDatum.getFullYear();
    var monat = pDatum.getMonth() + 1;
    var tag = pDatum.getDate();

    var add_monate_total = Math.abs( Number( pAnzahlMonate ) );

    var add_jahre = Number( Math.floor( add_monate_total / 12.0 ) );
    var add_monate_rest = Number( add_monate_total - ( add_jahre * 12.0 ) );

    if ( Number( pAnzahlMonate ) > 0 )
    {
        jahr += add_jahre;
        monat += add_monate_rest;

        if ( monat > 12 )
        {
            jahr += 1;
            monat -= 12;
        }
    }
    else if ( Number( pAnzahlMonate ) < 0 )
    {
        jahr -= add_jahre;
        monat -= add_monate_rest;

        if ( monat <= 0 )
        {
            jahr = jahr - 1;
            monat = 12 + monat;
        }
    }

    if ( ( Number( monat ) === 2 ) && ( Number( tag ) === 29 ) )
    {
        if ( ( ( Number( jahr ) % 400 ) === 0 ) || ( ( Number( jahr ) % 100 ) > 0 ) && ( ( Number( jahr ) % 4 ) === 0 ) )
        {
            tag = 29;
        }
        else
        {
            tag = 28;
        }
    }

    return new Date( jahr, monat - 1, tag );
}


testAddMonate : function( pDatum , pAnzahlMonate )
{
    var datum_js = fkDatum.getDateAusTTMMJJJJ( pDatum );
    var ergebnis = fkDatum.addDateMonate( datum_js, pAnzahlMonate );

    app.log( "addDateMonate( \"" + pDatum + "\", " + pAnzahlMonate + " ) = \"" + fkDatum.getStringAusDate( ergebnis ) + "\"" );
},


test1 : function()
{
    app.testAddMonate( "15.06.2010",    10 );
    app.testAddMonate( "15.06.2010",   -10 );
    app.testAddMonate( "15.06.2010",    37 );
    app.testAddMonate( "15.06.2010",   -37 );
    app.testAddMonate( "15.06.2010",  1234 );
    app.testAddMonate( "15.06.2010", -1234 );
    app.testAddMonate( "15.06.2010",  5620 );
    app.testAddMonate( "15.06.2010", -5120 );

}


评论


当大多数英语代码专家和Deutsch变量时,我发现它非常有用。 ;)

–伯恩哈德·霍夫曼(Bernhard Hofmann)
2014年10月3日13:17

#18 楼

有时像BIRT参数中那样由一个操作员创建有用的日期

我用1个月的时间做了:

new Date(new Date().setMonth(new Date().getMonth()-1));   


#19 楼

var a=new Date();
a.setDate(a.getDate()+5);


按照上述方法,您可以在Date函数中添加月份。