用原生Date对象转换GMT时间到指定时区

发布:elantion 日期:2021-08-02 阅读:154 评论:0

时间,这个大家都不陌生的东西,想必很简单?但深究之后,发现里面极其复杂,什么UTC、DST、GMT,各种奇怪的术语,让人措不着头脑,这里当然不会一个个展开来聊,只讲一下怎么把时间转换到另外个时区时间。

UTC时间标准和DST

我们现行的时区标准是UTC,所以经常会看到时间会以GMT+X的形式表式,例如:Mon Aug 02 2021 09:56:13 GMT+0800 (中国标准时间),表示英国格林威治标准时间2021年9月2日上午1点56分13秒再加上8个小时意思。据这种算法,是不是所有当地时间都可以用GMT时间加上时区就行?不是,还有个叫DST的东西,叫做夏时制,简单理解就是每年的夏天,时间都会往前拔一个小时,例如本来是早上九点的,就会显示成早上十点。但并不是所有国家都实行这种制度,所以,如果想把时间从GMT时区转换到另外个时间就会变得非常复杂,我们就可能维护一个DST的数据表来转换时区,像Moment.js、Day.js这类时间库就会这么做,但不想用依赖库的话,有没有简单的办法?

JS内置的Date对象

现代浏览器的Date对象已经非常强大了,基本的get+set功能都具备,例如:setHours,但却不支持setTimezone这种方法,也就是说,Date对象由始至终都只能在一个时区内运行(只能是当前浏览器的时区,用户设置,没法通过js转换),但幸运的是有个特殊的函数可以救场,就是toLocalString,利用他我们就能简单地把当前时区转换到期望的时区。

生成GMT时区时间对象

例如现在GMT时间是2021年8月1日4点32分45秒,我们先用Date.prototype.UTC方法快速生成一个GMT时间对象(注意:不能直接用setFullYear()setHours()等方法,这些方法都只能设置当前用户时区的时间,不是GMT,setUTCHours()之类的最好也别用),也可用Date.prototype.parse方法(注意要加上时区,不然会被视为当前时区,而非GMT),具体函数的使用方法参考MDN文档:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Date/UTC
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Date/parse
上代码:

const utcDate = new Date(Date.UTC(2021,7,2,4,32,45));

注意,月周期是0为基数的,所以8月的话,要传7。

生成对应时区时间

有了GMT的时间后,接下来就用Date.prototype.toLocalString来转换时区了,详细的说明文档请看:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleString
这里直接上代码:

utcDate.toLocaleString('en-GB', { timeZone: 'PRC', hour12: false });

解释下几个参数,en-GB是按英国英语格式生成的时间文本,所以这里生成的格式是这样:02/08/2021, 12:32:45。然后是timezone,这里填了PRC,也就是中国时区了。最后是hour12,就是转换成24小时制。

输出ISO标准时间

最后就是根据生成的时间文本,取出对应的时间了,我们简简单单地用正则取一下值就行:

const enDateReg = /(\d+)\/(\d+)\/(\d+),\s?(\d+):(\d+):(\d+)/;
const matches = Array.from(dateWithTz.match(enDateReg));
matches.shift();
const days = +matches[0];
let months = +matches[1];
months -= 1;
const years = +matches[2];
const hours = +matches[3];
const minutes = +matches[4];
const seconds = +matches[5];

到这里,你就能据你的需求拼接时间文本了。

最后

但如果反过来,怎么把一个时区的时区转所成GMT时区呢?这个目前来说还没有好的办法,只能使用Date.prototype.parse来识别,但不能具体到某个地区,我再查查资料有没有好的办法。