国漫手机壁纸

软件开发中的时区问题

摘要:本文总结几类项目中跟时区相关的问题,给大家分享一些基本的时区知识,以及如何在软件开发和测试中注意考虑时区因素,以避免因时区而导致系统功能的问题。场景一“小D,我发现调整不同的timezone(时区),咱们的KPI计算结果可能会有一天的误差…”“啊!我看看什么原因。”“嗯啦,K

NetSmell 出品

  摘要:本文总结几类项目中跟时区相关的问题,给大家分享一些基本的时区知识,以及如何在软件开发和测试中注意考虑时区因素,以避免因时区而导致系统功能的问题。

  场景一

“小D,我发现调整不同的 timezone(时区),咱们的 KPI 计算结果可能会有一天的误差…”

“啊!我看看什么原因。”

“嗯啦,KPI 务必是准确的,可不能有误差…”

“小Q,我知道啦!咱们的工单任务完成时间记录的是 UTC 时间,是考虑了 timezone 转换的,但是缺失信息的记录并没有考虑… ”

“为什么缺失信息不记录 UTC 时间呢?”

“因为缺失信息只记录日期,并没有时间。”

  蓝鲸项目的小Q和小D说的是啥呢?下面先来借助维基百科的解释来介绍时区和 UTC 的概念:

  • 时区(timezone) 时区是地球上的区域使用同一个时间的定义。以前,人们通过观察太阳的位置决定时间,这就使得不同的地方的时间有所不同(地方时)。1863 年,首次使用时区的概念,通过设立一个区域的标准时间部分地解决了这个问题。
  • UTC 协调世界时(英语:Coordinated Universal Time,法语:Temps Universel Coordonné,简称 UTC)是世界上调节时钟和时间的主要时间标准,它与 0 度经线的平太阳时相差不超过 1 秒。协调世界时是最接近格林威治标准时间(GMT)的几个替代时间系统之一。对于大多数用途来说,UTC 时间被认为能与 GMT 时间互换,但 GMT 时间已不再被科学界所确定。

  原来小Q和小D所在的蓝鲸项目正在开发一个全球性的系统,用户处于世界各地的不同时区。

  系统的工单处理流程中每个任务的完成因为有先后依赖关系,当时记录的是完成时刻的 UTC 时间,以防不同时区的用户完成依赖任务的时候产生冲突。

  比如:处于 UTC+0800 时区的用户S在当地时间 2019-02-28 09:30 处理了任务1,系统记录的时间是 UTC 2019-02-28 01:30,接下来处于 UTC-0800 的用户G在当地时间 2019-02-27 18:20 来处理任务2(必须晚于任务 1 完成),系统记录的是 UTC 2019-02-28 02:20,这个时间就不会有问题。

  在处理某个任务的时候如果发现有信息缺失,需要记录发现和收集到缺失信息的时间(可以是过去时间,由用户输入),而这个发现和收集完成的时间一般都是同一个用户来记录,本身不会有时区问题,另外这个时间跟工单处理流程中的任务完成时间并没有特别直接的关系,就对缺失信息部分只记录由用户输入的日期,而且是直接记录用户当地时间的日期,并没有记录时分秒,也没法根据 UTC 进行转换。

  这样,同一个日期,比如,在东部时区的 2019-04-28 可能就是西部时区的 2019-04-27,在西部时区的 2019-04-28 可能就是东部时区的 2019-04-29,前后有相差一天的可能性。

  但是,在做到 KPI 功能的时候,计算 KPI 需要结合工单任务处理时间和缺失信息记录时间,由于没有考虑时区问题,不同时区可能会造成 KPI 有一天的误差。

  场景二

“我们这里有个线上用户报过来一个 bug,合同签订日期签完以后打开编辑,在页面上变成了一个后一天的日期,没法保存”,R说。

“我们的合同签订日期最晚是签订当天的日期,的确不能用将来日期,这个业务上是这么要求的”,小B说。

“我们前端也有校验不允许存储将来日期。可是为什么会变成后一天的日期呢”,小D一脸迷惑。

“会不会还是 timezone 的问题?!” 小Q由于前一阵做 KPI 的时候测试时区相关问题花了不少精力去搞明白各种 case,对时区也有了不少的研究,对此比较敏感。

“那个报 bug 的用户设置是什么 timezone 的?” 小B问。

“是东 14 区(UTC+14:00)!” R 回答。

“竟然还有东 14 区,我一直以为都在正负 12 时区之间。”小Q很惊讶。

“那就对了!的确是 timezone 引发的问题!”小D沉思了一会,激动的跳起来。

这又是为什么呢?大家都在焦急地等待小D的解释。

  原来,对于这种没有时分秒要求的时间,我们系统统一用当天中午 UTC 的 12:00 存入 DB,这样做的原因是保证-12~+12 时区内都不会有问题,换算以后都是当天,但是没有考虑到正负 13、14 区的情况。

  下面我们来举例说明为什么东 14 区的会有问题。

  假设用户A和用户B分别处于东 12 区和东 14 区,A和B分别在当地时间的 2019 年 04 月 28 日的上午9:00 签订了合同,那么系统记录的时间都是 UTC 2019-04-28 12:00,在用户A的页面上显示的日期是 2019-04-28(UTC 2019-04-28 12:00 转换东 12 区的时间是 2019-04-28 24:00,这个时间的日期还是 4 月 28 日),而在用户B的页面上显示的日期应该是 2019-04-29(UTC 2019-04-28 12:00 转换东 14 区的时间是 2019-04-28 26:00,也就是 2019-04-29 02:00,这个时间的日期变成了 4 月 29 日)。

  如下表,用户A、B、C、D的合同签订时间都是当地时间 2019 年 04 月 28 日的上午9:00:

用户 表头 表头 表头 表头
A UTC+12:00 UTC 2019-04-28 12:00 UTC 2019-04-28 24:00 2019-04-28
B UTC+14:00 UTC 2019-04-28 12:00 UTC 2019-04-29 02:00 2019-04-29
C UTC-12:00 UTC 2019-04-28 12:00 UTC 2019-04-28 00:00 2019-04-28
D UTC-13:00 UTC 2019-04-28 12:00 UTC 2019-04-27 23:00 2019-04-27

  同理,用户C和D处于西部时区,从上表我们可以看到处于-12 的C的时间是跟实际日期一样,而处于-13 的用户D的时间则比实际时间早了一天,也是有问题的。

  至此,我们明白了为什么东 14 区会引起系统功能有问题。但是,一直以来以为时区都在正负 12 之间,为什么会有大于 +12 的时区呢?原来小D是早就知道的,他给我们解释了是下面两个原因:

  1. 有些跨国际日期变更线的地区,为了保证该地区所有地方的时间都在同一天,就把时区设置成了大于 +12 的,比如基里巴斯。
  2. 有些处于 +12 区的地区有夏令时,夏令时的时候时区会变成 +13,比如新西兰。

  其实,时区还有很多有意思的,有偏移量是半个小时的(如印度),还有 45 分钟的(如尼泊尔),不一定都是整点。更多详情可以参考维基百科的时区列表。

  场景三

客户P发过来一封邮件: “自 2017 年初以来,土耳其和白俄罗斯政府不再遵守夏令时(DST),因此这将改变这些国家的时区为 GMT+3,而不是 GMT+2。 目前,在我们系统伊斯坦布尔和明斯克都显示为 GMT+2。”

  这个问题听起来很简单,直接在 DB 里把对应的时区改一下就 ok 了。可是,正当小D准备去改数据的时候,发现了一个崩溃的事情:伊斯坦布尔和明斯克还跟另外两个地区的时区是绑定在一起的,见下图。当前系统中已经设置好的一些会议,没法判断真正需要的是哪个地区对应的时区… 已有数据无法修复!

  在此先不解释如何修复的数据问题。

  当时,我们正好要把时区引入到另外一个新系统,考虑到避免再出现类似的情况,采用了一个新的库,那就是每个地区对应一个时区。比如:GMT+08:00 分别有上海、乌鲁木齐、重庆、香港、新加坡等时区。

  场景四

“这个新的 timezone 里怎么有一些乱七八糟的时区?”

“乱七八糟的时区?”
“Etc 什么的,好像还正负矛盾的。比如:(GMT+08:00) Etc/GMT-8” 对时区敏感的小Q觉得这些时区不太顺眼。

“这个是咱们新的时区库引入的,不过的确挺奇怪的,不知道这个 Etc 时区是啥。” 小B也不是很明白。

  原来,这个时区表示法里时区名字都是用“区域/位置”来表示,比如“Asia/Shanghai”,而前面的“(GMT+08:00)”是表示相对于 GMT 的一个偏移量。前面对话中提到的“Etc/GMT-8”只是时区名字而已。那为什么叫这么奇怪的名字呢?

  下面引用维基百科的解释来说明:

  区域分为大陆,海洋或“Etc”三类。 目前使用的大陆和海洋是非洲,美洲,南极洲,北极,亚洲,大西洋,澳大利亚,欧洲,印度和太平洋。

  包括海洋的原因是一些岛屿很难连接到某个大陆,有些地理位置与一个大陆相连,在政治上却与另一个大陆相连。

  “Etc”属于特殊区域,用于某些管理区域,特别是用于表示协调世界时的“Etc/UTC”。 为了符合 POSIX 样式,以“Etc/GMT”开头的区域名称的标志与标准 ISO 8601 惯例相反。 在“Etc”区域,格林威治标准时间以西的区域有一个正号,而以东区域的名称有一个负号(例如“Etc/GMT-14”比 GMT 早 14 个小时。)

  总结

  对于国际化的软件系统来说,时区还是需要特别关注的。根据所经历项目出现的时区相关问题,尝试总结以下几点供大家参考。

  开发方面

  1. 对于具体操作执行时间,这种都是包含时分秒的具体时刻,存储全部采用 UTC 时间,在显示的时候根据时区偏移量转换为对应的当地时间。不同模块之间传递的时间参数要以 UTC 格式。
  2. 对于没有精确时间点的、由用户输入的日期,这种一般对时间要求没有那么精确,可以加上操作时候的时间戳存储到数据库。当然还需要结合业务对时间的具体需求来定不同方案。
  3. 时区数据库单独一个位置作为一个时区的方式可以更方便修改对应位置的时区,比如:“(GMT+08:00) Asia/Chongqing”,而不是“(GMT+08:00) Beijing,Chongqing,Hong Kong,Urumqi”
  4. 关于 UTC 时间的获取:可以用第三方工具获取客观的 UTC 时间,但是出于安全考虑,一般不用;而是直接从服务器获取 UTC 时间,所以需要保证服务器的客观时间点是正确的,如果服务器的客观时间点有错,将会导致计算出的时间有出入。
  5. 有时候需要自定义一些时间段供用户使用,这种需要特别注意夏令时的影响。

  测试方面

  1. 对于过去时间、未来时间的测试,一定要测不同时区跨度看是否满足条件能够正确使用。
  2. 显示时间:本地,不同时区切换能正确显示相应时间;输入时间:不同时区对应的日期边界值要重点考虑。如果只能输入未来时间,要考虑一些负时区的情况,比如 UTC-12, UTC-13 等;如果只能是过去时间,要考虑 UTC+12, UTC+14 等。
  3. 定时任务:如果设置成固定时间段(如:每隔一小时)执行,与时区无关;每天定点执行,要考虑不存在的或者多出的时间段。
  4. 日志文件:时间戳命名的文件,不会由于夏令时导致文件名重复;文件内容涉及时间部分,不同时区显示正确。
  5. 夏令时跳变时刻系统功能测试,对于多出来的一小时或者减少的一小时系统行为是否正常。
显示余下内容
相关文章:
  1. 信用卡 PIN 码很容易猜测
  2. 神经元簇发能模拟 AI 学习策略
  3. 蜘蛛丝可能根本不具有抗菌性质
  4. 佳能因禁止无墨水打印机扫描被起诉
  5. DeepMind盈利后开始「买买买」!收购机器人模拟平台MuJoCo,全面开源
  6. 分析师:新MacBook Pro搭载自家芯片,苹果利润率更高了
  7. 格芯提交上市申请IPO,筹资约26亿美元
  8. 美股周二:中概股普涨 阿里涨超6% 高途涨逾12%
  9. 搭配自研处理器与安卓12,谷歌新机Pixel 6起价599美元
  10. 摩根士丹利:马斯克有望凭SpaceX成首位万亿美元富豪
  11. 《鱿鱼游戏》助奈飞三季度新增用户翻倍,股价近新高
  12. DOTA 2又上热搜了 为什么这次大家到处刷“猛犸”?
  13. 多位游戏巨头联合希望美国政府监管盗版和作弊网站
  14. Google Play Data Safety开始接受开发者申请:2022年将强制执行
  15. 价格欺诈投诉引发公益诉讼 京东“划线价”格式条款须整改
 

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注