一般游戏都会在登录时把服务器时间戳同步到客户端,然后定时同步,保证客户端与服务器时间戳一致,在一些涉及时间的逻辑里,也以服务器时间为准。
所以即使玩家修改时间,也不会对游戏逻辑造成影响,但是如果玩家修改了时区,即便服务器时区固定在东八区,也会出问题。
今天项目海外版就遇到这么一个问题,逻辑上需要根据时间戳,计算出该时间戳当天的0点,可以保证传入的时间戳都是以服务器为准(东八区),代码写成:
function getZero( dCurTime )
local tbCurTime = os.date("*t", dCurTime)
local dToday0 = os.time({year=tbCurTime.year, month=tbCurTime.month, day=tbCurTime.day, hour=0, min=0, sec=0})
return dToday0
end
问题:
- 如果玩家在东八区且不修改时区,上述代码是不会有任何问题的,可以保证os.date和os.time计算跟服务器一致。
- 但是玩家修改时区后,首先os.date会得出不同与东八区的tbCurTime,偏差在24小时内(24个时区)。
- 因为求0点时间,hour、min和sec是固定为0,那么即使tbCurTime的year、month和day与东八区一致,但经过os.time运算后,dToday0与东八区也会有偏差。
解决办法:
如果只解决求0点时间戳问题,直接对dToday0进行加减就好了? 但是这又涉及os.date后跨天问题,这就需要对tbCurTime.day进行额外运算,这样处理起来太别扭了,而且以后遇到时区问题又得处理一遍。
期望:
不修改任何业务逻辑,通过修改接口解决时区问题。
我的方案:
思路:
1.首先确定当前服务器时区(我们项目是固定东八区,所以写死了8)
2.求出玩家客户端时间与UTC时间的差值(关键是os.date("!*t"))
3.根据1和2,得出玩家客户端时间与服务器时间的偏差值
4.然后重新定义os.date和os.time (os->xs) ,期望xs.date得到是东八区的日期table,xs.time能得到东八区的时间戳
5.夏令时:xs.date需要执行两次os.date(如果修改C++,应该能优化,或者有其他更好的方法?)
6.夏令时:xs.time只需要处理有传参的情况,而且无论参数表的isdst是true或nil,都赋值成false,因为东八区没有夏令时
7.疑问:那直接调用os.time()? 会直接返回客户端当前的时间戳,这个也合理,因为很多逻辑本身只需要得到当前时间戳,然后进行一些比较,不关心时差问题,即便与服务器不一致也能满足。
代码:
function getTimeZone()
local now = os.time()
local zone= os.difftime(now, os.time(os.date("!*t", now)))
return zone
end
e_dZoneDiff = 8*3600 - getTimeZone()
local xs = {}
function xs.date(format, time)
local tb = os.date(format, time + e_dZoneDiff)
if tb.isdst then
return os.date(format, time + e_dZoneDiff - 3600)
else
return tb
end
end
function xs.time(tab)
if tab then
tab.isdst = false
return os.time(tab) - e_dZoneDiff
else
return os.time()
end
end
网友评论