【问题标题】:How to treat date-input and time-input as local time, rather than universal time?如何将日期输入和时间输入视为本地时间,而不是通用时间?
【发布时间】:2020-04-13 23:47:12
【问题描述】:

用户输入2019-12-22 的日期输入会给出以下值:

  • input.value: "2019-12-22"
  • input.valueAsNumber: 1576972800000
  • input.valueAsDate: "Sat Dec 21 2019 16:00:00 GMT-0800 (Pacific Standard Time)"
    • 这个结果日期对象似乎是错误的
    • 当浏览器返回一个值时,它会将用户输入视为世界时
    • 所以日期对象的 utc 表示与输入显示给用户的相同
    • input.valueAsDate.getUTCDate() 返回 22,这是用户输入的内容
    • input.valueAsDate.getDate() 返回 21,而不是用户输入的内容
    • 因此我们得出结论,日期输入显示并接受 utc 时间,而不是当地时间

我们希望生成的 date.toString() 显示与日期输入中原始用户输入相同的结果

我们如何让用户与当地时间进行交互,然后在我们的脚本中获取正确的日期对象?

【问题讨论】:

  • 我认为您混淆了内部日期状态及其toString() 方法返回的表示。只需使用input.valueAsDate.toLocaleDateString(),它应该与用户输入的内容匹配。
  • 嘿,感谢您的光临!不幸的是:不,否定:这不是真的。刚刚在实验中验证,当用户输入2019-12-22 时,您对input.valueAsDate.toLocaleDateString() 的建议实际上返回"21/12/2019" -- 这与用户输入的内容不匹配 -- 假设日期输入接受本地时间然后返回一个日期本地输出匹配的对象是错误的——我知道,这很令人困惑,解决它的唯一方法是手动补偿时间偏移????????
  • 奇怪的是,这在 Firefox 中可以正常工作,但在 Chrome 中却不行(我假设您正在使用它)。
  • @str -- 没有人,我刚刚在 Firefox 中复制了这个:input.valueAsDate.toLocaleDateString() 返回"12/21/2019" -- 我们之间会不会有一些浏览器配置不同?肯定是我们之间的时区不同,你的时区是多少?也许您的时区偏移量不足以显示当天的差异 - 因此,对于我来说,在整个示例中使用时间输入可能会更明智,因为任何人都会重现该问题在格林威治 :)
  • 我将计算机的时区设置为您的 (PST),以验证 Firefox 和 Chrome 的行为。他们没有为我返回同样的东西。

标签: javascript date time timezone


【解决方案1】:

此问题是由 TC-39 决定将仅限日期的 ISO 8601 格式时间戳视为 UTC 造成的,而此时与 ISO 8601 保持一致并将它们视为本地更合乎逻辑。见Why does Date.parse give incorrect results?

简单的解决方案是手动解析字符串,不要使用内置解析器,作为至少一种当前实现,直到最近将 YYYY-MM-DD 解析为本地。此外,不要使用当前时区偏移量来调整时间值,因为这不允许偏移量的历史变化或可能的夏令时变化。

// Parse timestamp in YYYY-MM-DD format as local
function parseISOLocal(s) {
  let [y, m, d] = s.split(/\D/);
  return new Date(y, --m, d);
}

// Format date as YYYY-MM-DD local
function formatISOLocal(d) {
  let z = n => (n<10?'0':'') + n;
  return d.getFullYear() + '-' + z(d.getMonth()+1) + '-' + z(d.getDate());
}

let s = '2019-12-22';
let d = parseISOLocal(s);
console.log( d.toString());
console.log( formatISOLocal(d));

编辑

如果支持输入类型日期并且 YYYY-MM-DD 根据 ECMA-262 解析为 UTC,您可以使用 valueAsDate 和 UTC 方法。但是,并非所有浏览器都支持日期输入类型,并且并非所有解析器都会将该格式解析为 UTC。

不依赖输入类型日期并手动解析值,检查格式和有效性要可靠得多。这就是为什么通常使用日期小部件和库而不是内置日期功能的原因之一。

let inp = document.getElementById('dob');
let dobObj = inp.valueAsDate;
let dobStr = inp.value;

console.log('Value as date: ' + dobObj);   // Safari: null
console.log('Value as string: ' + dobStr); // 2018-06-15
&lt;input id="dob" type="date" value="2018-06-15"&gt;

【讨论】:

  • 非常感谢您的回答!我必须问,与其对input.value 进行字符串解析,不如使用输入日期对象的utc 组件更好?例如,input.valueAsDate.getUTCFullYear().getUTCMonth().getUTCDate()?
  • @ChaseMoskal — 这需要使用内置解析器和所需的变幻莫测。
  • 似乎valueAsDate 解析为UTC,所以它与new Date(inp.value) 相同(在支持的情况下)- 是吗?所以原来手动解析的方式还是比较好的选择。
【解决方案2】:

由于日期/时间输入元素接受用户输入为 UTC 时间,但我们想接受本地时间,我们必须手动补偿用户计算机上配置的本地时区偏移量

所以我们接受输入值作为数字,但在使用它创建 Date 对象之前将其偏移时区偏移量

// ascertain the timezone offset
const timeOffset = (new Date()).getTimezoneOffset() * 60 * 1000

// compensate for the weirdness
const milliseconds = input.valueAsNumber + timeOffset

// make a real date object
const date = new Date(milliseconds)

当通过toString()toLocaleDateString() 向用户显示时,生成的日期对象将与用户最初输入的时间相同。

【讨论】:

  • 使用该代码,您可以有效地将日期更改为不同的时间点。似乎您只关心日期表示,但在这里您实际上更改了内部状态,这很可能不是您想要的。
  • 因为输入元素接受用户输入作为 UTC 时间而不是本地时间,我们必须将用户输入偏移其时区的数量,以实现 toLocaleDateString() 匹配的日期对象用户的当地时间(以及他们最初作为输入输入的时间)——我知道这是违反直觉和令人困惑的,正是这种意想不到的行为导致我进行了如此多的调查——没有@ 似乎真的很愚蠢987654325@,这样输入既可以用作 UTC 也可以用作本地输入.. 没有奢侈,我们必须补偿自己??
  • 这不是一个好方法,因为它将应用当前时区偏移而不是实际日期的偏移,因此不允许基于历史或夏令时更改的不同偏移。此外,它依赖于内置的解析器,这是出了名的善变,至少有一个当前的浏览器将 YYYY-MM-DD 解析为本地,而不是 UTC。
  • @jonrsharpe -- 你怎么把我答案的小标题删了?如果我们不能使用标题,为什么我们可以制作标题?我认为这是对答案详细说明的技术的一个很好的总结——这不是很有用吗?什么时候适合使用标题降价功能?
猜你喜欢
  • 2015-12-17
  • 1970-01-01
  • 1970-01-01
  • 2014-02-22
  • 1970-01-01
  • 2021-11-08
  • 2018-07-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多