JavaScript日期中这种一字不漏的错误的根源是什么? [英] What is the source of this off-by-one error in a JavaScript date?
问题描述
我正在尝试编写一个函数,该函数将使用像 07/2020
这样的字符串,然后返回是否超过三个月.
I am trying to write a function that will take a string like 07/2020
and then return whether it is more than three months away.
我已经编写了一个函数 isMoreThan3MonthsHence
,我可以肯定地确定它可以正常工作:
I have written a function isMoreThan3MonthsHence
that I am reasonably sure works correctly:
const isMoreThan3MonthsHence = ({ utcYear, utcMonth },
now = new Date,
target = new Date(Date.UTC(utcYear, utcMonth)),
threeMonthsAway = new Date(now.valueOf()).setUTCMonth(now.getUTCMonth() + 3)) =>
(target > threeMonthsAway)
console.log(isMoreThan3MonthsHence({ utcYear: 2020, utcMonth: 7 })) // true (correct!)
当我尝试构造一个 Date
对象用于填充 isMoreThan3MonthsHence
的参数时,问题就来了.
The problem comes when I try to construct a Date
object to use to populate the arguments for isMoreThan3MonthsHence
.
const validate = (str,
[localMonth, localYear] = str.split('/'),
date = new Date(+localYear, (+localMonth)-1)) =>
isMoreThan3MonthsHence({ utcYear: date.getUTCFullYear(), utcMonth: date.getUTCMonth() })
// Note: input is one-based months
console.log(validate('07/2020')) // false (but should be true!)
我认为原因是在未指定时区的情况下在 validate
中更新 Date
会使用在提供的日期生效的本地时区BST(UTC + 1).
I think the reason is that new-ing up a Date
in validate
without specifying the timezone will use the local timezone in effect at the supplied date, which will be BST (UTC+1).
Wed Jul 01 2020 00:00:00 GMT+0100 (British Summer Time)
这实际上是UTC的 6月 30日2300小时.因此,从零开始,月份实际上是 5
.但是我不想要这种行为.我想要这样,所以指定7月实际上意味着UTC为7月.
This time is actually 2300hrs on June 30th in UTC. So the month is actually 5
in zero-based terms. But I don't want this behavior. I want it so specifying July actually means July in UTC.
我该如何解决?
推荐答案
除了混合UTC和本地日期外,您添加3个月的方式将导致对3月31日这样的日期的错误响应,其中添加3个月的时间就是增加月数将得出7月1日的日期.请参见在JavaScript中为日期添加月份.
Other than mixing UTC and local dates, the way you're adding 3 months will cause an incorrect response for dates like 31 March, where adding 3 months simply by incrementing the month number results in a date for 1 July. See Adding months to a Date in JavaScript.
因此 validate('07,2020')
如果在3月31日运行,将返回false.
So validate('07,2020')
will return false if run on 31 March.
要解决此问题,在添加月份时,请检查更新后的日期是否仍在该月的同一天,否则将其翻转,因此将其设置为上个月的最后一天.
To fix that, when adding months, check that the updated date is still on the same day in the month, otherwise it's rolled over so set it to the last day of the previous month.
function validate(s) {
let testDate = addMonths(new Date(), 3);
let [m, y] = s.split(/\D/);
return testDate < new Date(y, m-1);
};
function addMonths(date, months) {
let d = date.getDate();
date.setMonth(date.getMonth() + +months);
// If rolled over to next month, set to last day of previous month
if (date.getDate() != d) {
date.setDate(0);
}
return date;
}
// Sample
console.log('On ' + new Date().toDateString() + ':');
['07/2020', '04/2020'].forEach(
s => console.log(s + ' - ' + validate(s))
);
这篇关于JavaScript日期中这种一字不漏的错误的根源是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!