起因
很多時候我們會碰到這樣的需求,每天某個時候需要定時執行某個任務,比如定時發郵件、定時發推送消息等,而這個定時是針對當地時間的,比如每天中午12點發推送消息給玩家提醒可以上線領金幣啦。
然而,中國的12點和越南的12點就不是同一個12點,所以需要通過時區計算是否到了該執行任務的時候。
思路
先來看一下如何根據時區計算約定的時間。假設現在需要晚上20點,執行任務,時區爲西3時區。首先生成一個格林威治時間的20點
utcTime := time.Now().UTC()
targetTime :=time.Date(utcTime.Year(),utcTime.Month(),utcTime.Day(),
20, 0, 0, 0, utcTime.Location())
由於西3時區比格林威治時間慢3個小時,等西3時區20點的時候,格林威治時間就是20點再過3小時,對應的格林威治時間就是
targetTime.Unix() + 3 * 3600
而如果是東8時區到20點的時候,格林威治時間還差8小時纔到20點,則對應的時間是
targetTime.Unix() - 8 * 3600
細化
先對刷新時間的配置定義一個結構
type RefreshConfig struct {
TargetHour int
TargetMinute int
Targetsecond int
Offset int64
lastRefreshTime int64
}
定義了定時任務執行的時分秒,offset表示在格林威治時間對應的時分秒基礎上做多少偏移能得到本地的目標時間。offset定義如下
var zoneToOffset = map[string]int64{
"Z0": 0,
"E1": -1 * 3600,
"E2": -2 * 3600,
"E3": -3 * 3600,
"E4": -4 * 3600,
"E5": -5 * 3600,
"E6": -6 * 3600,
"E7": -7 * 3600,
"E8": -8 * 3600,
"E9": -9 * 3600,
"E10": -10 * 3600,
"E11": -11 * 3600,
"E12": 12 * 3600,
"W1": 1 * 3600,
"W2": 2 * 3600,
"W3": 3 * 3600,
"W4": 4 * 3600,
"W5": 5 * 3600,
"W6": 6 * 3600,
"W7": 7 * 3600,
"W8": 8 * 3600,
"W9": 9 * 3600,
"W10": 10 * 3600,
"W11": 11 * 3600,
"W12": 12 * 3600,
}
E8對應東8區,W3對應西3區,參照前文的計算方式。
通過一下代碼計算是否達到(或者超過了)當地的執行時間
func TimeIsUp(refreshConfig *RefreshConfig) bool {
targetTime := getTargerTime(refreshConfig.TargetHour,
refreshConfig.TargetMinute,
refreshConfig.Targetsecond,
refreshConfig.Offset)
return refreshConfig.lastRefreshTime < targetTime &&
time.Now().Unix() >= targetTime
}
定時任務執行以後,需要把lastRefreshTime設置爲當前時間。
完整內容查看github上的代碼
遺留問題
對於夏令時的計算,暫時沒找到比較好的辦法。