找回密码
 立即注册
查看: 386|回复: 5

【Unity游戏开发】全球游戏时区问题

[复制链接]
发表于 2022-4-25 10:34 | 显示全部楼层 |阅读模式
前言

最近被游戏中的各种时间问题烦得很,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
今日踩坑到此为止。。。
参考

游戏时区问题小解
发表于 2022-4-25 10:40 | 显示全部楼层
遇到半时区怎么处理的呢,比如 阿富汗
发表于 2022-4-25 10:43 | 显示全部楼层
涨知识了,没处理半时区,可能没面向那个地方吧
发表于 2022-4-25 10:47 | 显示全部楼层
有问题啊,ServerTimeZone - TimeUtils.GetLocalTimeZone() 是什么东西,前者是服务器时区,后者是本地与0时区相差的秒数
发表于 2022-4-25 10:53 | 显示全部楼层
ServerTimeZone 也是个秒数
发表于 2022-4-25 10:59 | 显示全部楼层
半时区不用特殊处理,os.difftime() 包括处理半时区
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-9-22 13:33 , Processed in 0.092494 second(s), 25 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表