JavaScript日期中这种一字不漏的错误的根源是什么? [英] What is the source of this off-by-one error in a JavaScript date?

查看:36
本文介绍了JavaScript日期中这种一字不漏的错误的根源是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试编写一个函数,该函数将使用像 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆