之前已经asked(严重) - 我不认为那篇文章中的答案真的解决了这个问题,然后它变得陈旧了。我将尝试再次提出问题,更明确地证明这个问题。
Javascript Date.setMonth()
的实现似乎没有遵循最不惊讶的原则。在浏览器控制台中尝试:
d = new Date('2017-08-31') // Set to last day of August
d.getMonth() // 7 - months are zero-based
d.setMonth(8) // Try to set the month to 8 (September)
d.getMonth() // 9 - October. WTF Javascript?
类似地:
d = new Date('2017-10-31')
d.getMonth() // 9
d.setMonth(8)
d.getMonth() // 9 (still?)
Linux上的Firefox看起来更糟糕 - 有时会在10月返回一个日期,而getMonth()
的结果与该月不匹配!
我的问题(我认为来自该关联问题的OP的问题)是如何始终如一地实施“下一个”/“上一个”月份功能,例如:一个约会者?有没有一种众所周知的做法,这并不会让用户感到惊讶,例如,在8月31日开始时跳过9月并点击“下一步”?从1月31日开始,目前更难以预测 - 你将在3月2日或3月3日结束,这取决于它是否是闰年!
我个人认为,最不意料的是转移到下一个/上个月的最后一天。但这需要setMonth()
实施来关注有问题的月份中的天数,而不仅仅是添加/减去固定的持续时间。根据{{3}}线程,moment.js
方法是在30天内添加/减去毫秒数,这表明库容易出现相同的不一致。
答案 0 :(得分:2)
这一切都很简单和逻辑。让我们举个例子来看看id是做什么的。
所以第一行
d = new Date('2017-08-31') // Set to last day of August
console.log(d); // "2017-08-31T00:00:00.000Z"
console.log(d.getMonth()); // 7 - months are zero-based

到目前为止一切顺利。下一步:您的评论说:// Try to set the month to 8 (September)
所以没有尝试。您要么将其设置为9月,要么不要。在您的示例中,您将其设置为十月。进一步说明。
d = new Date('2017-08-31') // Set to last day of August
console.log(d); // "2017-08-31T00:00:00.000Z"
console.log(d.getMonth()); // 7 - months are zero-based
d.setMonth(8) // Try to set the month to 8 (September)
console.log(d); // but now I see I was wrong it is (October)

所以好问题是为什么? From MDN
注意:Date被称为具有多个构造函数的构造函数 参数,如果值大于它们的逻辑范围(例如13是 作为月值提供或者为分钟值提供70),相邻 价值将被调整。例如。新日期(2013,13,1)相当于 新日期(2014,1,1),都创建了2014-02-01的日期(请注意 月是0基础)。与其他值类似:新日期(2013年,2,1,1, 70)相当于新的日期(2013年,2日,1日,1日,10日),它们都创造了一个 日期为2013-03-01T01:10:00。
所以说9月只有30天但是日期对象有31天。这就是为什么它给你10月而不是9月。
最简单的方法是将日期设置为月份的第一天。像这样的东西:
var d = new Date('2017-08-31') // Set to last day of August
// simplest fix take the date you have and set it to first day of month
d = new Date(d.getFullYear(), d.getMonth(), 1);
console.log(d); // "2017-08-31T00:00:00.000Z"
console.log(d.getMonth()); // 7 - months are zero-based
d.setMonth(8) // Set the month to 8 (September)
console.log(d.getMonth()); // get 8 it is (September)

答案 1 :(得分:0)
由于getMonth()
返回一个整数,你可以简单地在日期对象上实现一个生成器,它设置月份+ 1或-1,只要你不是分别在11月或0月。
function nextMonth(dateObj) {
var month = dateObj.getMonth();
if(month != 11) dateObj.setMonth(month + 1);
return dateObj;
}
function prevMonth(dateObj) {
var month = dateObj.getMonth();
if(month != 0) dateObj.setMonth(month - 1);
return dateObj;
}
如果您想匹配上个月的日期,可以使用对象查找表。
现在,对于你这个月的最后一天问题:
function getLastDayofMonth(month) {
var lookUp = {
0:31,
1:28,
2:30,
3:31
};
return lookUp[month];
}
//and then a revised version
function nextMonth(dateObj) {
var month = dateObj.getMonth();
var day = dateObj.getDate();
if(month != 12) dateObj.setMonth(month + 1);
if(getLastDayofMonth(month)<day)dateObj.setDate(getLastDayofMonth(month));
return dateObj;
}
答案 2 :(得分:0)
这应该可以用于递增月份,您可以使用类似的策略来减少。
// isLeapYear :: Number -> Boolean
const isLeapYear = ((err) => {
return yr => {
// check for the special years, see https://www.wwu.edu/skywise/leapyear.html
if (yr === 0) {
throw err;
}
// after 8 AD, follows 'normal' leap year rules
let passed = true;
// not technically true as there were 13 LY BCE, but hey.
if (yr === 4 || yr < 0 || (yr % 4)) {
passed = false;
} else {
if (yr % 400) {
if (!(yr % 100)) {
passed = false;
}
}
}
return passed;
};
})(new Error('Year zero does not exist, refers to 1 BCE'));
const daysInMonth = [
31,
28,
31,
30,
31,
30,
31,
31,
30,
31,
30,
31
];
// isLastDay :: Number, Number -> Boolean
const isLastDay = (d, m, y) => {
let dm = isLeapYear(y) && m === 1 ? 29 : daysInMonth(m);
return dm === d;
};
// getLastDay :: Number, Number -> Number
const getLastDay = (m, y) => isLeapYear(y) && m === 1 ? 29 : daysInMonth[m];
// incMonth :: Date -> Date
const incMonth = d => {
let dd = new Date(d.getTime());
let day = dd.getDate();
let month = dd.getMonth() + 1;
dd.setDate(5); // should avoid edge-case shenanigans
dd.setMonth(month);
let year = dd.getFullYear();
if (isLastDay(day, month, year)) day = getLastDay(month, year);
dd.setDate(day);
return dd;
};
答案 3 :(得分:0)
这是我提出的解决方案,据我所知,它似乎小而可靠。它不需要任何额外的数据结构,并依赖于setDate(0)
来选择边缘情况下的月份的最后一天。否则它只留下日期,这是我想要的行为。它还处理从一年到下一年(在任一方向)的包裹:
function reallySetMonth(dateObj, targetMonth) {
const newDate = new Date(dateObj.setMonth(targetMonth))
if (newDate.getMonth() !== ((targetMonth % 12) + 12) % 12) { // Get the target month modulo 12 (see https://stackoverflow.com/a/4467559/1454454 for details about modulo in Javascript)
newDate.setDate(0)
}
return newDate
}
注意我只是在targetMonth
高于或低于当月的情况下对此进行了测试,因为我使用的是“下一个”/“后退”按钮。它需要在任意月份测试更多用户。
答案 4 :(得分:0)
如果在添加和减去月份时使用 setMonth ,那么如果开始月份的日期在结束月份中不存在,则额外天数会导致日期为&#34;翻身&#34;到下个月,3月31日减1个月给3月2日或3日。
一个简单的算法是测试开始日期和结束日期,如果它们不同,请将结束日期设置为0,以便它转到上个月的最后一天。
这个问题的一个问题是,两次减去1个月可能不会产生与减去2个月一次相同的结果。 2017年3月31日减1个月给2月28日,减去另一个月给1月28日。从3月31日减去2个月,你将获得1月31日。
C&#39; est la vie。
function addMonths(date, num) {
var d = date.getDate();
date.setMonth(date.getMonth() + num);
if (date.getDate() != d) date.setDate(0);
return date;
}
// Subtract one month from 31 March
var a = new Date(2017,2,31);
console.log(addMonths(a, -1).toString()); // 28 Feb
// Add one month to 31 January
var b = new Date(2017,0,31);
console.log(addMonths(b, 1).toString()); // 28 Feb
// 29 Feb plus 12 months
var c = new Date(2016,1,29)
console.log(addMonths(c, 12).toString()); // 28 Feb
// 29 Feb minus 12 months
var c = new Date(2016,1,29)
console.log(addMonths(c, -12).toString()); // 28 Feb
// 31 Jul minus 1 month
var d = new Date(2016,6,31)
console.log(addMonths(d, -1).toString()); // 30 Jun
&#13;