游戏中的多时区问题

发表评论 阅读评论

随着全球化的进程,现在游戏基本上都会在海外发行。那么自然而然就会遇到一些问题,比如最重要的是语言翻译,而这里说另一个可能会被忽略的问题,面对不同时区的时间显示。

这里简单列几个时间

地区 时区 时间
中国 +8 12:00
日本 +9 13:00
美国纽约 -4 00:00
美国西雅图 -7 21:00(前一天)

有这个表可以看出面向不同地区的时间显示确实是个问题。

时间显示方案

虽然时间显示是个问题,但是我们总是要解决它。通常有3种方式来显示。

  1. 统一显示UTC时间
  2. 仅显示一个当地时间
  3. 不同时区显示不同的时间
统一显示UTC时间

作为程序很容易想到UTC时间(协调世界时),又称世界标准时间或世界协调时间。 由于UTC时间全世界通用,并且只有一个,所以如果时间是UTC12:00,那么不论在哪个时区都是这个时间,那么就自然能够准确的表述时间,不会造成认知的混乱。 这个解决办法看上去很好,但是UTC时间通常使用与科学界,并不被大众所熟知,更不能奢望游戏玩家能很好的将UTC时间转换为自己所在的当地时间。

所以UTC时间面向程序员使用尚可,但是不能面向普通大众使用。

仅显示一个当地时间

经常出国或者看国外的比赛或发布会的人都知道有时区的概念, 比如:NBA比赛或者苹果发布会等都会对外宣称以美国某个州的时间XX:XX开始。

这样大家就会转换到自己所在的当地时间是几点钟,以便安排好时间观看。

这个表述时间的方式虽然可以解决时间混乱的问题,但是对用户并不友好,因为大家通常都固定在一个时区,时间不会变来变去,大家都习惯于使用当地时间。

不同时区显示不同的时间

不同时区都显示本地时间,这样的用户体验最好。不需要用户做任何改变,一直在自己熟悉的时间下进行游戏。

现在就要用代码来实现这样的显示方式。

实现

offsetTimeZone = userTimerZone - serverTimeZone
displayTime = serverTime + offsetTimeZone

上面的伪代码看似很合理,其实忽略了时间显示的一个重要因素,那就是夏令时。

引用自维基百科夏时制词条

夏时制,另译夏令时间(英语:Summer time),又称日光节约时制、日光节约时间(英语:Daylight saving time),是一种为节约能源而人为规定地方时间的制度,在这一制度实行期间所采用的统一时间称为“夏令时间”。一般在天亮较早的夏季人为将时间调快一小时,可以使人早起早睡,减少照明量,以充分利用光照资源,从而节约照明用电。各个采纳夏时制的国家具体规定不同。

也就是说时间显示并不只由时区决定,而且还跟是否在夏令时间内有关系,还有更麻烦的,有的国家实行夏令制,有的国家不实行,实行夏令制的国家开始夏令制的时间还不一样。

如此看来要自己实现不同时间的显示确实比较困难,那么我们再将目光转向操作系统,其实操作系统已经很好的实现了不同国家/地区正确的时间显示。 既然操作系统以及实现了,我们就直接或间接的用操作系统的时间就好了。

由于UTC时间于时区和是否夏令制都没有关系,那么我们在内部使用UTC时间,只有向用户显示的时候显示用户当地时间,这样我们就不需要处理时区及夏令制的问题了。

让服务器传给客户端的时间都变成unix时间戳(unix时间戳本身就是UTC时间),然后客户端显示的时候转为本地时间。

local displayTime = os.date("%c", serverTime)
print(displayTime)

对于服务器,由于策划表不会配置UTC时间戳,需要从策划配置的人读的时间格式解析出具体的时间,比如2017-08-09 20:00,对于这个时间,只要策划统一配置为UTC时间或服务器所在时间,那么服务器按照这个规整来解析就好。最终会转换为内部使用的UTC时间。

有时策划会配置某个功能/活动会在20:00-21:00开放,连续1周都开放,对于不是具体的时间戳的时间限制问题,我们只要将这个小时也用时间戳也表示,使用的时候只使用时分秒部分即可,再考虑跨天的情况就好了。

具体代码可到我的gist找到

最后

比如某活动在用户本地时区的20:00-21:00开启,只要用户将自己的时间调整到20:00-21:00之间,就会发现该活动可以进入,至少在客户端显示已经开放了。为了解决这个这个,只需要在时间判断时不要直接使用本地时间,而是用服务器时间就可以了。只需要经服务器时间同步到客户端,并记录一个时间差,以后都使用本地实际+该时间差就可以了。为了防止玩家在游戏中修改本地时间,只要一定时间或重要的通信时同步下服务器时间就可以了。

local latency = 0
local ServerTime = {
    localTime = os.time(),
    serverTime = os.time(),
    -- 服务器时间-客户端时间
}

function ServerTime.setServerTime(time)
    latency = time - os.time()
    -- localTime = os.time()
    -- serverTime = time or os.time()
end

function ServerTime.getServerTime()
    return os.time() + latency
end

最后的最后,这里假设玩家在游戏中不会调整设备的时区,如果玩家在游戏中修改了时区,那么时间将不再正确。

所有代码已放到我的gist

  1. 本文目前尚无任何评论.
  1. 本文目前尚无任何 trackbacks 和 pingbacks.
回到顶部