时区问题杂谈
在今天,大部分手游制作团队的眼光都不再局限于中国国内市场,出海已经成了大部分公司手游的标配。但是在出海发行的时候,我们的产品难免会有一些“水土不服”的情况发生。时区,就是这其中的一个。
GMT,UTC以及UnixEpoch
- GMT:格林尼治标准时间
- UTC:协调世界时
- UnixEpoch:自UTC时间1970年1月1日0点0分0秒以来至今所经过的时间,通常用秒来表示
考虑到地球是个球,人们又把地球划分成24个时区。北京位于东8区,而我们一般说的北京时间可以表达为GMT+8或者UTC+8。因为通常来说,在不那么精确的情况下我们是可以认为GMT等于UTC的。
至于UnixEpoch或者说unix时间,很明显,这是一种对程序更为友好的时间表达方式。在python代码里面,直接调用time.time()得到的就是它的值,并且UnixEpoch与时区无关。同一时刻,在任何时区调用time.time()得到的值都应该相等。
夏令时(Daylight Saving Time)
海发除了要注意时区以外,还有可能遇到的一个问题就是夏令时。美国和大部分欧洲国家目前仍在使用夏令时。
引用python 2.7版本的官方doc里的一句话“DST rules are magic (determined by local law)”,在程序的角度换句话说就是,夏令时的开始与结束很难用统一的规则去控制(所以很多项目采用停服维护的方式去应对当地法律规定的夏令时开始与结束的情况)。
关于夏令时的介绍这里不再赘述,感兴趣的人可以自行上网搜索。我们只需要知道它生效时会发生什么就行了,下面就是一个例子。
1 | 洛杉矶在当地时间 2018年03月11日,02:00:00 时钟向前调整 1 小时 变为 2018年03月11日,03:00:00,开始夏令时 |
夏令时的规则看似简单,但是实际使用中稍不注意就会产生问题。举个列子,策划想要配个活动,活动在洛杉矶当地时间2018年3月11日1点举行,3点结束。如果没有考虑夏令时的情况直接在代码里面这么判断开始时间和结束时间
1 | import time |
从功能角度讲,策划的本意是设计一个当地时间1点到3点持续2个小时的活动,程序代码里实现的也是一个持续2个小时的活动。只不过当地的玩家会发现在当地使用了夏令时的时钟上活动一直持续到了当地时间4点才结束,难免会产生一些歧义与误解。
如何没有歧义的表达一个时间
日常使用中我们比较能接受的表达方式一般是这样的“Year-Month-Day Hour:Minute:Second”,如我现在写这篇博客的时间就可以表示为“2019-07-14 16:20:28”。这种表达方式有一个问题,就是只能在同一个时区使用,而地球是被划分成多个时区的。如果不带上时区以及是否使用夏令时的信息,这种写法是没有办法准确描述一个时间的(这里需要注意的是UnixEpoch时间,从来就没有任何歧义)。
几种常见的应用场景
在游戏中策划往往会设计需要多和时间相关的玩法或者活动,以下是我见过的比较常见的几种形式:
- 指定时间以后开始某活动,如某年某月某日几点整开服
- 每天,每周,每月,每年的固定时间段开始某活动,如每周固定时间段服务器维护
- 活动未开始时显示距某个时间段的时间,活动开始后显示距离结束剩余的时间
- 拥有固定cd的功能,比如领取奖励的cd是8小时
一般这些活动的时间都是策划配置的当地时间(至于为什么是当地时间,很简单,你总不能让玩家大半夜起来参加某个活动吧)。程序中一般的常见套路是将这个人类可读的时间转化成一个具体的可以比较大小的数(比如UnixEpoch),然后在请求到来需要条件判断的时候直接用当前时间比较就可以了。但是在判断一些比较复杂的条件时,会有一些坑。举个例子,有个活动是在每周五举行,那么自然程序这边需要判断一下当前时间是不是处于周五,于是有了以下错误代码:
1 | import time |
为什么说这一代码是错的呢?我们假设当前time.time()=0,则此时如果你在0时区,那当前的时刻应该恰好是1970年1月1日0点。但是如果你在北京(东8区),当前的时刻其实是1970年1月1日上午8点。假设又经过了17个小时,也就是17*3600=61200秒,此时time.time()=61200。在0时区当前时刻是1970年1月1日17点,但是在东8区却已经是1970年1月2日1点。对东8区而言,此时已经是周五了,但用上述代码运行得到weekday还是3(即周四),这明显是错误的。因此,正确代码其实应该是带上时区偏移来计算才对,如下。
1 | import time |
一点总结
最后是我的一些在实际业务中总结出来的经验:
1.time.time()得到的就是UnixEpoch,没有时区概念,任何时区在同一时刻调用该方法得到的值都相同。
2.两个不同地方的时间之间如果要比较先后关系必须带有时区信息。换句话说就是:不同时区的时间进行比较时,最简单的方式是转换成对应时刻的0时区的时间。
3.“2019-7-15 12:00:00”这个时间,需要有时区和是否使用夏令时信息才有准确意义
4.计算一个当地的时间(今天的秒数,这周周五的时间戳,今天0点的时间戳等)时,需要用time.time()加上当地时区的偏移来计算。
5.夏令时是真的傻X
install_url
to use ShareThis. Please set it in _config.yml
.