游戲時(shí)區(qū)問(wèn)題小解

由于時(shí)區(qū)拗踢、夏令時(shí)的存在脚牍,游戲內(nèi)的時(shí)間顯示/計(jì)算都要考慮時(shí)區(qū)問(wèn)題并進(jìn)行相應(yīng)處理。時(shí)間計(jì)算不用說(shuō)巢墅,要排除玩家本地時(shí)區(qū)影響诸狭,只以服務(wù)器時(shí)區(qū)為準(zhǔn)進(jìn)行計(jì)算。時(shí)間顯示有兩種方案:

  1. 根據(jù)服務(wù)器下發(fā)的utc時(shí)間戳君纫,按玩家手機(jī)本地設(shè)置的時(shí)區(qū)進(jìn)行適配顯示驯遇,這樣對(duì)于經(jīng)常往返于不同時(shí)區(qū)的玩家很友好(雖然這類(lèi)玩家很少),玩家只要修改手機(jī)時(shí)區(qū)蓄髓,游戲內(nèi)的時(shí)間顯示就以該時(shí)區(qū)為準(zhǔn)了叉庐。然而這種方案通常會(huì)碰到問(wèn)題,比如游戲內(nèi)活動(dòng)圖片里寫(xiě)死了日期会喝,時(shí)間陡叠,顯然就無(wú)法根據(jù)玩家手機(jī)時(shí)區(qū)適配顯示。
  2. 根據(jù)服務(wù)器時(shí)區(qū)進(jìn)行統(tǒng)一顯示是更好的方案肢执,如果是國(guó)內(nèi)上線游戲枉阵,可以統(tǒng)一顯示東八區(qū)時(shí)間,這樣就可以保證圖片里的時(shí)間信息是正確的预茄。這種方案也有個(gè)附帶好處岭妖,當(dāng)玩家不自知地將時(shí)區(qū)設(shè)為其他時(shí)區(qū),時(shí)間卻設(shè)成東八區(qū)時(shí)間時(shí)(我們項(xiàng)目?jī)?nèi)有個(gè)策劃的手機(jī)就是這樣設(shè)置的-_-||),游戲內(nèi)的時(shí)間顯示"看起來(lái)"還是正確的昵慌。
    簡(jiǎn)單總結(jié)假夺,游戲內(nèi)的時(shí)間顯示/計(jì)算最好都以服務(wù)器時(shí)區(qū)為準(zhǔn),而各種語(yǔ)言關(guān)于時(shí)間函數(shù)的api斋攀,都是以本地時(shí)區(qū)計(jì)算返回結(jié)果的已卷,以Lua為例,Lua標(biāo)準(zhǔn)庫(kù)中提供的時(shí)間函數(shù) os.time()和os.date()淳蔼,這兩個(gè)函數(shù)傳入和返回的時(shí)間table就是以本地時(shí)區(qū)為準(zhǔn)的侧蘸。

os.time()

  • 原型:os.time ([table])
  • 解釋:按table的內(nèi)容返回一個(gè)時(shí)間值(數(shù)字),若不帶參數(shù)則么使用當(dāng)前時(shí)間作為table內(nèi)容鹉梨,其中table中可以包含的字段有:year, month, day, hour, min, sec, isdst讳癌,其他字段將會(huì)被忽略。

os.date()

原型:os.date ([format [, time]])
解釋:返回一個(gè)按format格式化日期存皂、時(shí)間的字串或表晌坤。
參數(shù)格式:

  • 由原型可以看出可以省略第二個(gè)參數(shù)也可以省略兩個(gè)參數(shù),只省略第二個(gè)參數(shù)函數(shù)會(huì)使用當(dāng)前時(shí)間作為第二個(gè)參數(shù)旦袋,如果兩個(gè)參數(shù)都省略則按當(dāng)前系統(tǒng)的設(shè)置返回格式化的字符串骤菠,做以下等價(jià)替換 os.date() <=> os.date("%c")。
  • 如果format以“疤孕!”開(kāi)頭商乎,則按格林尼治時(shí)間進(jìn)行格式化。
  • 如果format是一個(gè)“*t”祭阀,將返一個(gè)帶year(4位)鹉戚,month(1-12), day (1--31)专控, hour (0-23)抹凳, min (0-59),sec (0-61)踩官,wday (星期幾, 星期天為1)境输, yday (年內(nèi)天數(shù))和isdst (是否為日光節(jié)約時(shí)間true/false)的帶鍵名的表;
  • 如果format不是“*t”蔗牡,os.date會(huì)將日期格式化為一個(gè)字符串

服務(wù)器時(shí)區(qū)

要以服務(wù)器時(shí)區(qū)進(jìn)行時(shí)間計(jì)算,編碼思路就是要計(jì)算出本地與服務(wù)器的時(shí)區(qū)差嗅剖,調(diào)用os.time()辩越、os.date()時(shí)進(jìn)行補(bǔ)償。

-- 服務(wù)器時(shí)區(qū)為東八區(qū)
local ServerTimeZone = 3600 * 8

-- 獲取客戶端本地時(shí)區(qū)
function TimeUtils.GetLocalTimeZone()
    local now = os.time()
    local localTimeZone = os.difftime(now, os.time(os.date("!*t", now)))
    return localTimeZone
end

服務(wù)器時(shí)區(qū):對(duì)于國(guó)內(nèi)服務(wù)器信粮,服務(wù)器時(shí)區(qū)可以直接硬編碼成東八區(qū)黔攒,如果考慮做國(guó)際化,可以由服務(wù)器進(jìn)行下發(fā)該值,根據(jù)地區(qū)設(shè)置不同服務(wù)器時(shí)區(qū)值督惰。
本地時(shí)區(qū):在lua里沒(méi)有直接獲取本地時(shí)區(qū)的api不傅,但通過(guò)os.date("!*t", os.time()),可以獲取格林尼治的時(shí)間table赏胚,再以本地時(shí)區(qū)解析table獲取時(shí)間戳访娶,該時(shí)間戳與os.time()時(shí)間戳相減即為時(shí)區(qū)秒數(shù)差值。

假設(shè)現(xiàn)在游戲內(nèi)有個(gè)功能入口要在游戲開(kāi)服第二天0點(diǎn)開(kāi)啟觉阅,如果不考慮時(shí)區(qū)問(wèn)題崖疤,編碼如下,當(dāng)玩家修改本地時(shí)區(qū)時(shí)典勇,計(jì)算得出的時(shí)間戳是不同的劫哼。這樣玩家就可以通過(guò)修改本地時(shí)區(qū),讓功能提前開(kāi)啟割笙。

-- 獲取開(kāi)服第二天0點(diǎn)時(shí)間戳
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})

因此可以對(duì)os.date()权烧、os.time()做一層封裝,傳入/返回的時(shí)間table都以服務(wù)器時(shí)區(qū)為標(biāo)準(zhǔn)咳蔚。本地時(shí)區(qū)就完全不會(huì)影響時(shí)間計(jì)算邏輯了豪嚎。

-- 替代os.date函數(shù),忽略本地時(shí)區(qū)設(shè)置谈火,按服務(wù)器時(shí)區(qū)格式化時(shí)間
-- @param format: 同os.date第一個(gè)參數(shù)
-- @param timestamp:服務(wù)器時(shí)間戳
function TimeUtils.Date(format, timestamp)
    local timeZoneDiff = ServerTimeZone - TimeUtils.GetLocalTimeZone()
    return os.date(format, timestamp + timeZoneDiff)
end

-- 替代os.time函數(shù)侈询,忽略本地時(shí)區(qū)設(shè)置,返回服務(wù)器時(shí)區(qū)時(shí)間戳
-- @param timedata: 服務(wù)器時(shí)區(qū)timedate
function TimeUtils.Time( timedate )
    local timeZoneDiff = ServerTimeZone - TimeUtils.GetLocalTimeZone()
    return os.time(timedate) - timeZoneDiff
end

-- 獲取開(kāi)服第二天0點(diǎn)時(shí)間戳
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})

通過(guò)TimeUtils.Date()糯耍、TimeUtils.Time()替代os.date()扔字、os.time(),業(yè)務(wù)邏輯處理時(shí)間計(jì)算時(shí)温技,只需考慮服務(wù)器時(shí)區(qū)即可革为,即使日后游戲進(jìn)行國(guó)際化,只需根據(jù)地區(qū)修改ServerTimeZone即可舵鳞,對(duì)業(yè)務(wù)層沒(méi)有影響震檩。


夏令時(shí)

如果我們生活在一個(gè)簡(jiǎn)單美好的世界,時(shí)區(qū)問(wèn)題就此解決了蜓堕,然后勤勞智慧的人民們抛虏,為了節(jié)能(sheng)減排(qian),又發(fā)明了夏令時(shí)套才,以上代碼在實(shí)行夏令時(shí)的國(guó)家地區(qū)里迂猴,計(jì)算結(jié)果可能不對(duì)。

夏令時(shí)背伴,又稱“日光節(jié)約時(shí)制”沸毁,英文全稱Daylight Saving Time峰髓,簡(jiǎn)稱DST。大白話來(lái)說(shuō)就是從前有人覺(jué)得大家伙晚睡晚起息尺,導(dǎo)致晚上照明用電太久浪費(fèi)錢(qián)携兵,夏天天亮得早,就提倡大家伙夏天時(shí)一起把時(shí)鐘調(diào)快1個(gè)小時(shí)掷倔,你不是習(xí)慣晚上12點(diǎn)才睡覺(jué)嗎眉孩?那都把表調(diào)快1小時(shí),變相地讓你提前1小時(shí)睡覺(jué)勒葱,從而實(shí)現(xiàn)節(jié)省減排浪汪。夏令時(shí)制度是以國(guó)家為單位來(lái)執(zhí)行的,每個(gè)國(guó)家一年里夏令時(shí)生效的時(shí)段還不一樣凛虽,目前全世界有近110個(gè)國(guó)家每年要實(shí)行夏令時(shí)死遭。以英國(guó)倫敦為例,英國(guó)倫敦位于零時(shí)區(qū)凯旋,與中國(guó)東八區(qū)相差8個(gè)時(shí)區(qū):在不實(shí)行夏令時(shí)的日子里呀潭,與中國(guó)確實(shí)是相差8小時(shí);實(shí)行夏令時(shí)后至非,與中國(guó)只相差7小時(shí)了钠署。

扯了很多夏令時(shí)的概念,回到時(shí)區(qū)處理問(wèn)題荒椭,在計(jì)算時(shí)區(qū)差時(shí)谐鼎,就需要判斷玩家本地設(shè)置時(shí)區(qū)是否正在實(shí)行夏令時(shí),如果是則在原計(jì)算結(jié)果上再加3600秒趣惠。os.date()返回的時(shí)間table里帶有isdst字段狸棍,isdst=true表示正在使用夏令時(shí)。因此前面代碼優(yōu)化如下:

-- 獲取客戶端本地時(shí)區(qū)
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

針對(duì)國(guó)內(nèi)上線游戲做以上的時(shí)區(qū)處理味悄,基本就沒(méi)問(wèn)題了草戈。真正做不同區(qū)服國(guó)際化時(shí),服務(wù)器與本地時(shí)區(qū)的夏令時(shí)因素都要考慮進(jìn)來(lái)做處理侍瑟,等以后有機(jī)會(huì)踩坑了再記錄吧唐片。

最后一句題外話,感謝國(guó)家統(tǒng)一了時(shí)區(qū)涨颜,感覺(jué)國(guó)家廢除了夏令時(shí)费韭。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市咐低,隨后出現(xiàn)的幾起案子揽思,更是在濱河造成了極大的恐慌袜腥,老刑警劉巖见擦,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件钉汗,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡鲤屡,警方通過(guò)查閱死者的電腦和手機(jī)损痰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)酒来,“玉大人卢未,你說(shuō)我怎么就攤上這事⊙吆海” “怎么了辽社?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)翘鸭。 經(jīng)常有香客問(wèn)我滴铅,道長(zhǎng),這世上最難降的妖魔是什么就乓? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任汉匙,我火速辦了婚禮,結(jié)果婚禮上生蚁,老公的妹妹穿的比我還像新娘噩翠。我一直安慰自己,他們只是感情好邦投,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布伤锚。 她就那樣靜靜地躺著,像睡著了一般尼摹。 火紅的嫁衣襯著肌膚如雪见芹。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,125評(píng)論 1 297
  • 那天蠢涝,我揣著相機(jī)與錄音玄呛,去河邊找鬼。 笑死和二,一個(gè)胖子當(dāng)著我的面吹牛徘铝,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播惯吕,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼惕它,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了废登?” 一聲冷哼從身側(cè)響起淹魄,我...
    開(kāi)封第一講書(shū)人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎堡距,沒(méi)想到半個(gè)月后甲锡,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體兆蕉,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年缤沦,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了虎韵。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡缸废,死狀恐怖包蓝,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情企量,我是刑警寧澤测萎,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站届巩,受9級(jí)特大地震影響绳泉,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜姆泻,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一零酪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧拇勃,春花似錦四苇、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至瓣赂,卻和暖如春榆骚,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背煌集。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工妓肢, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人苫纤。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓碉钠,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親卷拘。 傳聞我的和親對(duì)象是個(gè)殘疾皇子喊废,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容