博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
游戏时区问题小解
阅读量:4992 次
发布时间:2019-06-12

本文共 3684 字,大约阅读时间需要 12 分钟。

由于时区、夏令时的存在,游戏内的时间显示/计算都要考虑时区问题并进行相应处理。时间计算不用说,要排除玩家本地时区影响,只以服务器时区为准进行计算。时间显示有两种方案:

  1. 根据服务器下发的utc时间戳,按玩家手机本地设置的时区进行适配显示,这样对于经常往返于不同时区的玩家很友好(虽然这类玩家很少),玩家只要修改手机时区,游戏内的时间显示就以该时区为准了。然而这种方案通常会碰到问题,比如游戏内活动图片里写死了日期,时间,显然就无法根据玩家手机时区适配显示。
  2. 根据服务器时区进行统一显示是更好的方案,如果是国内上线游戏,可以统一显示东八区时间,这样就可以保证图片里的时间信息是正确的。这种方案也有个附带好处,当玩家不自知地将时区设为其他时区,时间却设成东八区时间时(我们项目内有个策划的手机就是这样设置的-_-||),游戏内的时间显示"看起来"还是正确的。
    简单总结,游戏内的时间显示/计算最好都以服务器时区为准,而各种语言关于时间函数的api,都是以本地时区计算返回结果的,以Lua为例,Lua标准库中提供的时间函数 os.time()和os.date(),这两个函数传入和返回的时间table就是以本地时区为准的。

os.time()

  • 原型:os.time ([table])
  • 解释:按table的内容返回一个时间值(数字),若不带参数则么使用当前时间作为table内容,其中table中可以包含的字段有:year, month, day, hour, min, sec, isdst,其他字段将会被忽略。

os.date()

原型:os.date ([format [, time]])

解释:返回一个按format格式化日期、时间的字串或表。
参数格式:

  • 由原型可以看出可以省略第二个参数也可以省略两个参数,只省略第二个参数函数会使用当前时间作为第二个参数,如果两个参数都省略则按当前系统的设置返回格式化的字符串,做以下等价替换 os.date() <=> os.date("%c")。
  • 如果format以“!”开头,则按格林尼治时间进行格式化。
  • 如果format是一个“*t”,将返一个带year(4位),month(1-12), day (1--31), hour (0-23), min (0-59),sec (0-61),wday (星期几, 星期天为1), yday (年内天数)和isdst (是否为日光节约时间true/false)的带键名的表;
  • 如果format不是“*t”,os.date会将日期格式化为一个字符串

服务器时区

要以服务器时区进行时间计算,编码思路就是要计算出本地与服务器的时区差,调用os.time()、os.date()时进行补偿。

-- 服务器时区为东八区local ServerTimeZone = 3600 * 8-- 获取客户端本地时区function TimeUtils.GetLocalTimeZone()    local now = os.time()    local localTimeZone = os.difftime(now, os.time(os.date("!*t", now)))    return localTimeZoneend

服务器时区:对于国内服务器,服务器时区可以直接硬编码成东八区,如果考虑做国际化,可以由服务器进行下发该值,根据地区设置不同服务器时区值。

本地时区:在lua里没有直接获取本地时区的api,但通过os.date("!*t", os.time()),可以获取格林尼治的时间table,再以本地时区解析table获取时间戳,该时间戳与os.time()时间戳相减即为时区秒数差值。

假设现在游戏内有个功能入口要在游戏开服第二天0点开启,如果不考虑时区问题,编码如下,当玩家修改本地时区时,计算得出的时间戳是不同的。这样玩家就可以通过修改本地时区,让功能提前开启。

-- 获取开服第二天0点时间戳local nextDayTable = os.date("*t", openServerTime + 86400)local nextDayZeroHourTime = os.time({year=nextDayTable.year, month=nextDayTable.month, day=nextDayTable.day, hour=0,min=0,sec=0})

因此可以对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: 服务器时区timedatefunction TimeUtils.Time( timedate )    local timeZoneDiff = ServerTimeZone - TimeUtils.GetLocalTimeZone()    return os.time(timedate) - timeZoneDiffend-- 获取开服第二天0点时间戳local nextDayTable = TimeUtils.Date("*t", openServerTime + 86400)local nextDayZeroHourTime = TimeUtils.Time({year=nextDayTable.year, month=nextDayTable.month, day=nextDayTable.day, hour=0,min=0,sec=0})

通过TimeUtils.Date()、TimeUtils.Time()替代os.date()、os.time(),业务逻辑处理时间计算时,只需考虑服务器时区即可,即使日后游戏进行国际化,只需根据地区修改ServerTimeZone即可,对业务层没有影响。


夏令时

如果我们生活在一个简单美好的世界,时区问题就此解决了,然后勤劳智慧的人民们,为了节能(sheng)减排(qian),又发明了夏令时,以上代码在实行夏令时的国家地区里,计算结果可能不对。

夏令时,又称“日光节约时制”,英文全称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 localTimeZoneend

针对国内上线游戏做以上的时区处理,基本就没问题了。真正做不同区服国际化时,服务器与本地时区的夏令时因素都要考虑进来做处理,等以后有机会踩坑了再记录吧。

最后一句题外话,感谢国家统一了时区,感觉国家废除了夏令时。

转载于:https://www.cnblogs.com/leoin2012/p/10970497.html

你可能感兴趣的文章
unity 判断 是手机还是平板
查看>>
VisualStudio2015单步调试
查看>>
【进程资源】监视进程资源
查看>>
团队成员效绩评定
查看>>
【數據結構】哈工大實驗一:一元多项式(代碼以及報告)
查看>>
简单理解Socket
查看>>
Hortonworks HDP Sandbox定制(配置)开机启动服务(组件)
查看>>
DHCP Option 60 认识
查看>>
浅析连续子向量,子数组和(一维,二维)问题
查看>>
C/C++中各种类型int、long、double、char表示范围(最大最小值)
查看>>
Linux环境下Eclipse + Tomcat + MySQL 配置J2EE开发环境的方法
查看>>
机器学习实战:第九章 树回归
查看>>
while(~scanf("%d %d",&a,&b))和while(scanf("%d %d",&a,&b)!=EOF)
查看>>
使用vs code开发纸壳CMS并启用Razor智能提示
查看>>
动态投资回收期Pt小于计算期n
查看>>
Python模拟登入豆瓣网,并爬取小组信息
查看>>
初识Jsp,JavaBean,Servlet以及一个简单mvc模式的登录界面
查看>>
@import与link的区别与选择
查看>>
ORA-14411 该 DDL 不能与其他 DDL 并行运行处理办法
查看>>
C#筛法求出范围内的所有质数
查看>>