浏览器原生能力系列 - 时间、日期和时区

在做国际化项目中,服务器端时间转换为用户本地时间一直是个问题,其实浏览器本身已经提供了这样的能力,而且它可能比想象中更加容易。

时区(Time Zone)是地球上的区域使用同一个时间定义。1884年在华盛顿召开国际经度会议时,为了克服时间上的混乱,规定将全球划分为24个时区。
在中国采用首都北京所在地东八区的时间为全国统一使用时间。 - 百度百科

时区,做国际化项目中肯定会遇到,因为不同人所在地区不同,时间也是不同的,从这点来说,时间和地区也是相对的。传统处理时间的方式是将本地时间序列化成类似“2014-04-14 13:10:28”这样的字符串,这样的缺点显而易见,一是将时间转换为这样的字符串,需要额外的代码,因为日期格式不一定,这样的需求作为 Javascript 的语言根本不会去处理,换而言之,这样的字符串虽然方便人类阅读,但是却不利于计算机处理;二是这样只记录了本地时间,当一个项目走向国际化就需要一个单独的时区字段来进行转换,同样的另外一个时区的人提交数据的时间也需要额外的转换工作,这样既增加了冗余,同时增加了整体系统复杂度。

操作系统的解决方案

无论是 Windows、Linux 还是 macOS,他们也面临着相同的问题,所以操作系统的设置中都会有一项“日期和时区”配置,这样的配置也为所有应用程序所共用,包含浏览器,所以浏览器是自带时区支持的。

以 macOS 为例:

Time zone

sudo systemsetup -gettimezone
> Password:
> Time Zone: Asia/Shanghai

浏览器中的时区

浏览器通过 Date instance 的 getTimezoneOffset() 方法提供了获取当前环境中时区的能力,它返回了当前时区和 UTC 时区按分钟的偏移量,即当前时间加上偏移量,就是 0 时区的格林尼治时间,如果返回的偏移量是负数,则是东区时间,正数则是西区时间。

const now = new Date();                // 获取当前时间
now.getTimezoneOffset() / 60;          // 获取时区偏移量
> -8                                   // -8 即为东8区

时间序列化 - Date serialization

序列化也叫对象字符串化,其实浏览器已经提供了一个简单方便的方法进行时间序列化,无需拼凑字符串,序列化后的数据可以除了在浏览器环境中,还可以在各类数据库中直接使用,它的名字叫做 JSON.stringify()。 ;-)

JSON.stringify(now)
> ""2017-04-14T05:11:42.247Z""

其实序列化后的时间,会被转换成格林尼治时间。

这样的字符串可以直接以 Date 类型保存到数据库中,取出来的时候,使用 JSON.stringify() 进行序列化,自动就会转换成这样的日期格式,而不用再手工处理。

以 Sequelize 为例:

const aModel = require('./models/im-a-model');
aModel.default.findOne().then(model => console.log(JSON.stringify(model.dataValues, null, 2)));
> {
>   "id": 1,
>   "name": "某某某",
>   "createdAt": "2017-02-14T19:32:15.000Z",
>   "updatedAt": "2017-04-06T18:08:53.000Z"
> }

时间反序列化 - Date deserialization

将字符串恢复为对象,即为反序列化,常规我们在将 JSON 对象反序列化时,最常用的还是 JSON.parse() 方法,同样的 Date 也可以这么做。

var dateStr = JSON.stringify(now);
JSON.parse(dateStr);
> "2017-04-14T05:11:42.247Z"

不过此时它还是个字符串,因为 JSON 序列化成字符串后已经丢失了对象的原始信息,要将该日期重新赋值到 Date 对象中才可以更加方便地使用。

new Date(JSON.parse(dateStr))
> Fri Apr 14 2017 13:11:42 GMT+0800 (CST)

我们可以看到时间已经从格林尼治时间恢复为东8区的北京时间了。

结论

以上代码已经简单地说明了时间对象的序列化、保存、反序列化的步骤,希望能对应用开发带来一些帮助。

在做开发过程中,通过原始的对象进行操作带来的便利性比拼凑字符串要强很多,原始的数据或者对象也很方便进行二次加工,这是浏览器自身就具备的能力。

题外话 - 时间对象的操作

说到时间对象,其实 Javascript 本身的能力是很弱的,即使是将日期对象转换为一个人类可阅读的、指定格式的字符串都很不容易,遇到这样问题的开发者并不只有我们,所以有人开发了一个很强大的时间日期处理库 - moment.js,同样是将日期对象转换为字符串,它的 format() 方法异常强大,例如:

moment(now).format('YYYY-MM-DD HH:mm:ss');
> "2017-04-14 13:11:42"

它同样提供了增减时间、两个时间判断大小等等一系列功能,moment 对象在使用时比 Date 更加方便。

目前它已经和 lodash 一起成为了前端项目开发的基础库之一了,更多功能等待你的发掘。

版权所有丨转载请注明出处:https://kxq.io/archives/浏览器原生能力系列-时间日期和时区