游戏时区问题小结

问题

游戏国际化后,会有不同时区的玩家在一起玩,但是开发商不可能为每种不同时区都设定一套逻辑,而且玩家可以自己修改设备的时区,因此游戏内的时间不能简单的依据玩家设备的时区来显示,要以服务端设定的时区为标准,所有玩家按这个时区来进行各种活动。

解决

时间显示有两种方案:
  1. 服务端给时间戳,客户端根据自己的时区展示。如果有图片,图片上写死了预告时间,那这个时间就可能和客户端时间对不上了,因为活动是按服务端时区来配置的,而客户端和服务端的时区可能不一样。
  2. 服务端给时间戳,客户端根据服务端的时区展示。对有的玩家来说不直观呗。
    所以其实游戏中大部分活动应该都还是用倒计时来体现。

实践(Lua)

两个主要方法
os.time([table])

按table返回一个时间戳,这个是标准时间戳。 table的完整时间戳减去utc的1970.1.1,再减去当前时区和utc的时间差(比如东八区,那就是减8小时),得到标准时间戳,即从开始计时到指定时间流逝的时间,所有时区都一样(废话)。
table = {year, month, day, hour, min, sec, isdst}

os.date([format[,timestamp]])

返回按format格式化日期,时间的字串或者表。
如果format以“!”开头,则按格林尼治时间进行格式化。即1970.1.1+timestamp的结果,否则是1970.1.1+时区差+timestamp。
如果format是“*t”,则返回table,否则返回格式化的字符串。

换算关键点

各种语言关于时间函数的api,都是以本地时区计算返回结果的, 参数也认为是以本地时区为基础。

  1. 得到服务端时区
  2. 得到本地时区
  3. 明确time方法的原理。接收date,输出时间戳。认为接收的date是基于设备时区的日期-1970.1.1-时区,返回标准时间戳。
  4. 明确date方法的原理。接收标准时间戳,输出基于当前时区的日期。接受的标准时间戳+1970.1.1+时区。
夏令时

光节约时间,本地时区加一个时区+3600

测试代码
local zeroTimestamp = os.time({year=1970, month=1, day=1, hour=8}) --0
print(zeroTimestamp) --输入当前时区的时间table,返回基于当前时区的时间戳。也就是会减掉8个小时的时区差,这个时间戳就可以直接用于各种时区了,相当于从开始计时起,经过了多长时间,这个时间,不论哪个时区都是一样的(再加上时区差,就是各个时区表现上的时间)
print(os.date("%c", zeroTimestamp), zeroTimestamp) --8点
print(os.date("!%c", zeroTimestamp)) --0点

local curTimestamp = os.time() --从开始计时到现在流过的时间。基于当前时区的时间戳,如果要自己计算来获得时间,还要加上时区的时间差
print(os.date("%c"), curTimestamp) --当前时间 会自动加上时区的时间差 1970+时间戳+时区差
print(os.date("!%c"), curTimestamp) --utc时间 直接拿1970+时间戳得到结果

local serverTimeZone = 6*3600
local function GetServerTimestamp()
	return os.time()
end

local function GetLocalTimeZone()
	local now = os.time()
	local localTimeZone = os.difftime(now, os.time(os.date("!*t", now)))
	local isdst = os.date("*t", now).isdst
	localTimeZone = isdst and localTimeZone+3600 or localTimeZone
	return localTimeZone
end

--传入和传出的时间都按服务器时区

--获取服务端时间 注意os.date会给时间戳加上当前时区差,所以把时间戳减去时间差,就得到标准时间,再加上想要的时区,就得到想要时区的时间
--输入时间戳
local function GetServerDate(timestamp)
	return os.date("%c", timestamp - GetLocalTimeZone()+serverTimeZone)
end

--输入基于服务器时区的时间table,得到标准时间戳
local function GetTime(serverDate)
	--+local得到日期的完整时间戳, -serverTimeZone得到标准时间戳, 这个标准时间戳便可以应用于各种时区的os.time
	--要想清楚时间戳是什么,其实就是跟时分秒一样,转化成数值,然后减去1970.1.1
	return os.time(serverDate) + GetLocalTimeZone() - serverTimeZone
end

--获取下个服务端0点的标准时间戳
local function GetNextDayZeroTime()
    --获取服务器date
    local serverDate = os.date("*t", os.time() + 86400)
    local nextDayZeroTime = GetTime({year = serverDate.year, month = serverDate.month, day = serverDate.day, hour = 0, min = 0, sec = 0})
    return nextDayZeroTime
end


print("获取指定标准时间戳的服务器日期", GetServerDate(GetServerTimestamp()))
print("获取指定服务器日期的标准时间戳", os.date("%c", GetTime(os.date("*t"))))
print("获取下个服务端零点的标准时间戳", GetNextDayZeroTime())
print("获取服务端时间下个零点日期", GetServerDate(GetNextDayZeroTime()))
print("获取服务端时间下个零点日期(客户端时区)", os.date("%c", GetNextDayZeroTime()))
参考

游戏时区问题小解-立航

原文地址:https://www.cnblogs.com/nickcan/p/12995068.html