开发一个时间小程序

前言

跟异国他乡的朋友们微信聊天的时候,经常面临时差的问题。我每次想要确定对方现在是几点,总是要口算一下,有时忘记具体时差,或者涉及跨天,还得打开浏览器查一下,很不方便。有什么方法可以把朋友们所在城市的时间集中起来随时供自己查看呢?于是想到了微信小程序。找了找市面上的时间小程序,不是功能太杂就是小广告太多,不满意。天音:为什么不自己动手量身打造一个呢?

行动起来。

首先快速明确需求

很简单:

  1. 需要展示时间的城市初定:加州、纽约,再加北京做对比
  2. 需要显示具体的时分秒,和年月日
  3. 需要实时变化
  4. 在其他国家也能正确展示时间

然后创建项目开撸

怎么创建和前期的准备就不在这里展开了,相信不少人都熟悉。如果不熟悉小程序开发的可以参考官网 或者我的另一篇文章如何开发微信小程序 ,上面有对如何开发小程序的简明扼要的的介绍。

关键逻辑

这个小程序的核心是时间的处理。如何得到其他地区的时刻信息?

这还不简单?
先获取本地时刻,然后加上或者减去另外一个地点与国内(北京时间)的时差(小时),最多再处理一下跨天的情况,不就得到其他地点的时刻了?

我一开始也是这么想的,做完觉得还挺美,准备提交的时候,突然意识到问题:我时差全是基于北京时间计算的,换在其他国家访问,获取的本地时间已经不是北京时间了,时差应该变才对,写死了时差可还行?!发布一个只能在国内使用的鸡肋时间工具,可不是我的风格!

捣鼓一阵,新方案出炉:

  1. 想办法获得零时区的时间
  2. 获取不同地区与零时区的时差(时区)
  3. 用零时区的时间加减与零时区的时差(时区),得到各地的绝对时间

1. 获得零时区的时间

零时区,也叫中时区,位于英国格林威治本初子午线上。该时区的地方时,叫做格林威治时间,也叫世界时。

我们不能直接获得格林威治时间,但是我们可以获得本地与格林威治的时间差:

1
const diff = new Date().getTimezoneOffset() // 单位为分钟

然后根据本地时间和时间差获得格林威治时间:

1
const absTime = new Date().getTime() + diff * 60 * 1000;

2. 查询各地时区

格林威治本初子午线将地球划分为东西两个半球,格林威治本初子午线为零时区,往西依次为西一区到西十一区,往东依次为东一区到东十一区,西十二区和东十二区重合成为东西十二区,一共划分了24个时区,每个时区相差正好是1个小时。

北京是东八区,纽约是西五区,加州是西八区。

完整时区地图:
timezone-map.jpg

3. 计算各地的绝对时间

东时区的时刻比零时区快,西时区的时刻比零时区慢,所以东时区为正,西时区为负,所有时间计算记得转换为毫秒。

1
let localTime = new Date(absTime + timeZone * 60 * 60 * 1000);

获取任何时区的绝对时间的完整核心代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* timeZone: 东n区为正,西n区为负, 单位为小时
*/
const getFullTimeInfo = (timeZone, country, spliter) => {

//获取本地时间与格林威治时间的时间差(注意是分钟,记得转换)
const diff = new Date().getTimezoneOffset();

//根据本地时间和时间差获得格林威治时间
const absTime = new Date().getTime() + diff * 60 * 1000;

//根据格林威治时间和各地时区,得到各地时区的时间
let localTime = new Date(absTime + timeZone * 60 * 60 * 1000)

return {
time: formatTime(localTime, spliter)
};
}

发布

很快,第一版就完成了。

world-time-v1.0.0

刚开始这个样子略丑,有点裸奔的赶脚。不过第一版最主要是核心功能,简陋的界面只是暂时的。

给当地的朋友检验确定时间展示正确后,提交代码、提交审核,2天后收到审核通过的通知(吐槽腾讯的审核效率😓),然后在小程序管理平台点击发布,哦了。

扫描二维码,打开小程序,然后收藏。以后要看时间了,微信主界面向下一拉,打开我的时间工具,一眼就看到想要知道的时间信息,确实比之前便捷多了。功能虽然简单,界面虽然简陋,但是妥妥滴满足我的需求。

迭代

用了一阵子,觉得样式啥的还是得丰富丰富,于是花了一些时间做了一次改版,实时时间以时钟效果展示,并且修改了布局,顺便重构了一下代码,便于新增地区。

world-time-v2.0.0

嗯,效果似乎还行~

改BUG

前几天跟澳洲的朋友聊天,聊着聊着居然发现了我的程序的一个潜在BUG。

那天是4月4日的早晨(北京时间),我跟朋友吐槽我的一个疑惑:查询悉尼时区为东十区(即与北京相差2小时),但是为啥查询悉尼时间却与北京相差3小时(所以我当时程序中是把悉尼作为东十一区来计算的)。朋友说:是的没错,我们这里现在在使用夏令时,等夏令时结束就恢复2个小时时差了。然后一查,今年澳洲夏令时将在4月5号凌晨3点结束。。。

也就是说,距离这个BUG发作还有不到一天的时间。。。

马上打开电脑,改BUG。。。

根据资料,获得美国和澳大利亚的夏令时规则:

  • 美国
    每年的3月第二个星期日02:00:00,时钟向前调整1小时,变为03:00:00,开始夏令时。
    每年的11月第一个星期日02:00:00,时钟向后调整1小时,变为01:00:00,结束夏令时。

  • 澳大利亚
    每年的10月第一个星期日02:00:00,时钟向前调整1小时,变为03:00:00,开始夏令时。
    每年的4月第一个星期日03:00:00,时钟向后调整1小时,变为02:00:00,结束夏令时。

关于夏令时,也挺有意思,有空我会另开一个篇幅来专门讲述。

将夏令时的判断逻辑加上:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
* timeZone: 东n区为正,西n区为负, 单位为小时
*/
const getFullTimeInfo = (timeZone, country, spliter) => {

//获取本地时间与格林威治时间的时间差(注意是分钟,记得转换)
const diff = new Date().getTimezoneOffset();

//根据本地时间和时间差获得格林威治时间
const absTime = new Date().getTime() + diff * 60 * 1000;

//根据格林威治时间和各地时区,得到各地时区的时间
let localTime = new Date(absTime + timeZone * 60 * 60 * 1000)

+ // 考虑夏令时
+ // judgeDST 是我封装好的一个判断夏令时的方法
+ const isDST = judgeDST(localTime, country);
+ if (isDST) {
+ localTime = new Date(absTime + (timeZone + 1) * 60 * 60 * 1000)
+ }

return {
time: formatTime(localTime, spliter).split(':').slice(0,2).join(':'),
isDST
};
}

有了现在的版本:
world-time-v2.1.0

以后对这个小工具我还会不断优化,会越来越灵活,比如支持地区选择,这样每个人都可以定制自己的时差表了。可以期待一下哦~

最后附上小程序二维码,扫一扫即可体验。

world-time-qr-code.jpg


还是毛爷爷说得好:自己动手丰衣足食。

Happy coding :)

文章同时发表于公众号「前端手札」,喜欢的话可以关注一下哦。

qianduanshouzha-gzh.png