|
前言
最近被游戏中的各种时间问题烦得很,QA提一堆bug,其实问题都是一个,就是不同时区在游戏中的时间显示不同,导致有的活动开启时间受到影响,造成bug。
比如现在游戏内有个功能入口要在第二天0点开启,如果不考虑时区问题,玩家修改本地时区时,计算得出的时间戳是不同的。这样玩家就可以通过修改本地时区,让功能提前开启。
要解决这个问题,想了一下,大一统才是王道,统一一下时间的显示,这样,不管在哪个时区,大家的时间显示都是一样的。 解决方案
要做到时间显示一致,
- 首先要知道服务器所在的时区,这个由服务器在登录时候下发通知。
服务器时区:对于国内服务器,服务器时区可以直接硬编码成东八区,如果考虑做国际化,可以由服务器进行下发该值,根据地区设置不同服务器时区值。 -- 服务器时区:服务器下发,这里写死东八区,做测试
local ServerTimeZone = 3600 * 82. 接着要知道自己所在的时区
本地时区:在lua里没有直接获取本地时区的api,但通过os.date("!*t", os.time()),可以获取格林尼治的时间table,再以本地时区解析table获取时间戳,该时间戳与os.time()时间戳相减即为时区秒数差值。 -- 获取客户端本地时区
function TimeUtils.GetLocalTimeZone()
local now = os.time()
local localTimeZone = os.difftime(now, os.time(os.date("!*t", now)))
return localTimeZone
end3. 拿到了2个时区,我们只要相减,就能算去大一统时间显示需要的偏移,每次在显示时间的时候都加上这个偏移,这样大家显示的时间就一致了,为了大家编码方便,可以对os.date()、os.time()做一层封装,传入/返回的时间table都以服务器时区为标准。本地时区就完全不会影响时间计算逻辑了。
-- 替代os.date函数,忽略本地时区设置,按服务器时区格式化时间
-- @param format: 同os.date第一个参数
-- @param timestamp:服务器时间戳
function TimeUtils.Date(format, timestamp)
local timeZoneDiff = ServerTimeZone - TimeUtils.GetLocalTimeZone()
return os.date(format, timestamp + timeZoneDiff)
end
-- 替代os.time函数,忽略本地时区设置,返回服务器时区时间戳
-- @param timedata: 服务器时区timedate
function TimeUtils.Time( timedate )
local timeZoneDiff = ServerTimeZone - TimeUtils.GetLocalTimeZone()
return os.time(timedate) - timeZoneDiff
end问题2
解决完这个问题,愉快关闭了一堆bug,然鹅,我们的QA还有点细心,分别调了不同时区,发现了一个问题,在调到一些夏令时国家的时区的时候,显示的时间总是比其他时区快一个小时,实力给我上了一课。
夏令时,又称“日光节约时制”,英文全称Daylight Saving Time,简称DST。
大白话来说就是从前有人觉得大家伙晚睡晚起,导致晚上照明用电太久浪费钱,夏天天亮得早,就提倡大家伙夏天时一起把时钟调快1个小时,你不是习惯晚上12点才睡觉吗?那都把表调快1小时,变相地让你提前1小时睡觉,从而实现节省减排。
夏令时制度是以国家为单位来执行的,每个国家一年里夏令时生效的时段还不一样,目前全世界有近110个国家每年要实行夏令时。以英国伦敦为例,英国伦敦位于零时区,与中国东八区相差8个时区:在不实行夏令时的日子里,与中国确实是相差8小时;实行夏令时后,与中国只相差7小时了。 在计算时区差时,就需要判断玩家本地设置时区是否正在实行夏令时,如果是则在原计算结果上再加3600秒。os.date()返回的时间table里带有isdst字段,isdst=true表示正在使用夏令时。因此前面代码优化如下:
-- 获取客户端本地时区
function TimeUtils.GetLocalTimeZone()
local now = os.time()
local localTimeZone = os.difftime(now, os.time(os.date("!*t", now)))
local isdst = os.date("*t", now).isdst
if isdst then localTimeZone = localTimeZone + 3600 end
return localTimeZone
end
今日踩坑到此为止。。。
参考
游戏时区问题小解 |
|