考虑时区了吗?

更新日期: 2021-11-20阅读: 941标签: 日期

前端工程师们,在拿到一个日期/时间数据的时候,你是怎么处理的呢?有没有考虑时区的问题呢?

也许你会说:嗯,没怎么关注时区,不过我测过,没问题。

我能怎么说呢?可能你还没遇到国外的用户吧!

被偷走的一天

如果拿到一个日期字符串 "2021-11-17",为了方便计算和处理数据,可能会先把它封成一个 Date 对象:

const s = "2021-11-17";
const d = new Date(s);

现在使用 d.getDate() 看会得到什么?—— 是 17,没毛病!

其实 —— 怎么说呢 —— 这是因为你在中国。如果这段程序在浏览器运行,而浏览器正好位于加拿大,还会是 17 吗?不妨试试。

在 Windows 下进入时间设置,把时间改为“太平洋时间(美国和加拿大)”或者其他 UTC-XX:00 的时区。再到浏览器里去看看,会发现 d.getDate() 得到了 16!

如果是在 Linux 下,也可以重新设置系统时间,然后用 Node 控制台来查看,如下图:


为什么少了一天?

浏览器始终使用本地时间

要问为什么少一天,不妨看看 d 到底是个什么样的数据(记得先把时区改回来哦!)

d.toString();         // Wed Nov 17 2021 08:00:00 GMT+0800 (中国标准时间)
d.toLocaleString();   // 2021/11/17 上午8:00:00

你看,new Date("2021-11-17") 创建日期对象的时候,是把 2021-11-17 当作 UTC 00:00:00 来创建的对象。而这个时间在中国 ,就是 2021-11-17 08:00:00。同理,如果是在 -06:00 的加拿大中部,它会是 2021-11-16 18:00:00,这时候 .getDate(),当然会得到 16。

在处理时间这个问题上,浏览器处理得简单粗暴,就是根据系统的时区设置来按照本地时间进行处理。如果我们给的是一个精确的时间,浏览器这样处理没毛病。毕竟时间是世界的,在这个时间点上,中国是早晨,加拿大是黄昏 —— 问题在于,Date 对象不能只描述日期,还要描述时间,它自动按 UTC 补了时间数据之后,我们就可能得到非预期的结果。

带时间部分的字符串

那么,如果拿到的表示时间的字符串带时间的呢?是不是会准确一点 …… 试试吧:

// 为了方便查看,注释里的结果数据采用了简洁的描述
new Date("2021-11-17");           // 2021-11-17 08:00 +08:00
new Date("2021-11-17 00:00:00");  // 2021-11-17 00:00 +08:00
new Date("2021-11-17 09:00:00");  // 2021-11-17 09:00 +08:000

注意第 2 条。在我们的常识中,2021-11-17 00:00:00 和 2021-11-17 应该是同一个时间吧?后者不带时间部分,所以默认它是一天的开始,也就是 00:00:00 —— 问题就在这里,Date 对象默认它是 UTC 的 0 时,而不是本地时间的 0 时。但如果给了时间,Date 会把它按本地时间来处理 —— 莫名其妙地就出现了时差。

该如何做到无差别

因为 new Date() 的时候是按本地时间来解析的,所以在不同地区的浏览器里,同一个表示日期时间的字符串会被解析成不同的时间 —— 你看,中国的早晨 9 点和加拿大的早晨 9 点绝对不是同一个时间对吧!

如果我们希望 new Date() 拿到的是同一个时间怎么办?

说起来也挺简单的,只要拿到字符串包含时区信息就行了,比如符合 RFC3339 标准的时间表示 2021-11-17T09:00:00+08:00。RFC3339 这里就不详述了,只需要简单地理解为

  • 日期部分是 yyyy-MM-dd 格式
  • 时间部分是 HH:mm:ss 或 HH:mm:ss.sss 格式
  • 日期和时间之间使用 T 连接
  • 时区部分是 Z 或者 ±HH:mm 格式,其中 Z 表示无时差,即 UTC 时间。比如下面两个时间是在同一个时间点上

    2021-11-17T09:00:00+08:00

    2021-11-17T01:00:00Z

很幸运 Date 能识别符合 RFC3339 标准的时间描述,所以用上例两个字符串创建的时间是相同的

const d1 = new Date("2021-11-17T09:00:00+08:00");
const d2 = new Date("2021-11-17T01:00:00Z");
console.log(d1.getTime() === d2.getTime());

还要注意什么

道理已经讲明白了,但是实际操作中还是会出现一些奇奇怪怪的问题。而出现这些问题的原因,归根结底还是没在注意到“时区”。比如,我问几个问题,看能不能搞得明白?

  • 拿到简单表示日期/时间的字符串,不符合 RFC3339 标准,比如前面提到的 "2021-11-17 09:00:00"。请问他是 UTC 时间还是本地时间?
  • 如果是本地时间 —— 它是应用服务器的本地时间?还是数据库服务器的本地时间?还是录入时间的用户所在时区的本地时间……?

你看,简单的一个时区问题,并不只是前端单方面的问题,它可能是整个系统设计的时候就因为忽略了时区概念而造成的问题。

如果在系统设计的时候就把时区问题考虑进去,所有存储和传输都使用相同时区的时间描述,事件就会变得好办得多。当然,如果直接使用 UTC 时间,或者 Unix Time Stamp 之类精确的描述会更理想。

也许有人觉得,浏览器确实只支持本地时间,但是我们可以用 Moment.js 库,可以用 Day.js 库,可以用 date-fns 库 …… 是的,你可以用各种各样的工具,但是还是得搞明白拿到的时间描述是准确无歧义的。再强调一次,准确的时间描述有:

  • 带时间信息的字符串,比如符合 RFC3339 标准的字符串描述;
  • 基于某个特定时间的秒级、毫秒级或更细微级别的时间偏移值,比如 Unix Time Stamp。

我们的程序生在中国,但要面向世界 —— 你考虑时区了吗?

作者:边城
原文来自:https://segmentfault.com/a/1190000040990370




链接: https://fly63.com/article/detial/10839

解决javaScript在不同时区new Date()显示值不同问题

在日期格式化时遇到的问题,日期格式化方法在最下面。如果在中国时区 formatDate(‘2019-07-09‘) 结果是 ‘2019-07-09’,如果 在夏威夷时区 utc-10:00 或者别的时区 formatDate(‘2019-07-09‘) 结果是 ‘2019-07-08’

JS获取当前月的最后一天

为了更好的讲这节的内容,提示一个 JS 处理日期的小技巧,想获取上个月最后一天,只需要设置SetDate参数为0即可。使用 JS 获取当前月的最后一天,咱们通常的思路先获取下个月的第一天

Js如何获取某一天所在的星期?

我们这里来获取今天所在星期的始末日期,我们可以通过(new Date).getDay()来获取今天是星期几,然后再通过这个减去或者加上一定的天数,就是这个星期的开始日期和结束日期。

vue 循环取值日期格式化,字符串截取处理

用vue取值页面遍历时,每次都搜索js日期格式化和字符串长度截取,这次记录下来,以后从这里直接拿即可。html代码如下

js中Date的构造函数解读

javascript中的内置对象是我们经常会用到的,那么今天我们就来说说Date的四种构造方法吧,new Date()这是我们最常使用也最熟悉不过的Date对象的构造方法了,通过无参数的构造函数我们可以默认获取到一个代表实例化时的Date对象

js如何获取某一天所在的星期?

我们这里来获取今天所在星期的始末日期,我们可以通过(new Date).getDay()来获取今天是星期几,然后再通过这个减去或者加上一定的天数,就是这个星期的开始日期和结束日期。

ElementUI日期选择器时间选择范围限制

ElementUI是饿了么推出的一套基于vue2.x的一个ui框架。官方文档也很详细,这里做一个element-ui日期插件的补充。官方文档中使用picker-options属性来限制可选择的日期,这里举例子稍做补充。

js获取一段时间内的间隔日期

js获取指定时间范围内指定间隔天数的所有日期,前端js,已知开始时间a、结束时间b和间隔天数c,要求取在a-b这两个时间范围内、间隔c天的所有日期。

前端的各种日期操作

虽然现在处理日期方面已经有了很成熟的也很好用的库,例如(momentjs和date-fns),但是在实际开发中,我们有时候可能并不需要整个库。所以我就在下面整理了在前端开发时对日期时间的各种操作,也算是比较全的了

Safari Date() 函数对日期时间字符串(yyyy-MM-dd HH:mm:ss) 提示NaN的问题

今天发现一个奇怪的问题,在iPhone使用 safari 选择定时发布文章到OSC,选择时间后提示不是合法的时间,判断时间的代码如下:在Chrome下会输出 pass,在Safari会输出 isNaN,根据 ECMAScript 5 ISO-8601 format support: 的说法

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!