iT邦幫忙

2021 iThome 鐵人賽

DAY 29
1
Modern Web

Let's Go! 解剖Go server開發到部署的過程系列 第 29

day 29 - timestamp & 定時執行

我們系統的溝通都是透過timestamp, 包含API參數跟資料庫的儲存都是以timestamp為主,因為timestamp是唯一的, 不管身處哪個時區針對同時間所換出來的timestamp都會是一樣的, 這樣在API溝通時才不會造成資料錯亂的狀況。
但是有時候還是會有需要日期跟時間的需求, 像是排程在美東時間00:00時要發送通知等等, 這時候就需要搭配timezone來決定要使用的時區的時間。如果跟我一樣身處在台灣, 那可以用Asia/Hong_Kong時區, 如果在美東的話可以使用America/Caracas, 不過這邊要注意的是, 很多地區有日光節約時間, 這時候轉換日期時就會踩到日光節約時間的雷, 所以美東時間我們採用的是America/Caracas 而不是America/New_York

這邊示範Go 幾個關於時間的操作

  • 取當前日期&時間

        /** 取當前時間 **/
        locHongKong, _ := time.LoadLocation("Asia/Hong_Kong")
        now := time.Now()
        fmt.Println("Asia/HongKong 當前時間 > ", now.Format("2006-01-02 15:04:05"), ", timestamp > ", now.Unix())
    
  • 取當前美東時區日期&時間

        /** 更改時區 Caracas **/
        locCaracas, _ := time.LoadLocation("America/Caracas")
        fmt.Println("America/Caracas 當前時間 > ", time.Now().In(locCaracas).Format("2006-01-02 15:04:05"), ", timestamp > ", time.Now().In(locCaracas).Unix())
    
  • 往前一天, 往前一個月, 往前一年

        nextYear := now.AddDate(1, 0, 0)
        fmt.Println("Asia/HongKong 加一年 > ", nextYear.Format("2006-01-02 15:04:05"), ", timestamp > ", nextYear.Unix())
    
        nextMonth := now.AddDate(0, 1, 0)
        fmt.Println("Asia/HongKong 加一個月 > ", nextMonth.Format("2006-01-02 15:04:05"), ", timestamp > ", nextMonth.Unix())
    
        nextDay := now.AddDate(0, 0, 1)
        fmt.Println("Asia/HongKong 加一天 > ", nextDay.Format("2006-01-02 15:04:05"), ", timestamp > ", nextDay.Unix())
    
  • 指定當前時區 明天00:00 的時間日期

        /** 明天 00:00 **/
        nextDay, err := time.ParseInLocation("2006-01-02 15:04:05", nextDay.Format("2006-01-02")+" 00:00:00", locHongKong)
        if err != nil {
            fmt.Println("err:", err)
        }
        fmt.Println("明天 00:00 > ", nextDay.Format("2006-01-02 15:04:05"), ", timestamp > ", nextDay.Unix())
    
  • 定時排程, 排程通常會應用在預做、檢查或同步資料, 固定每天幾點開始預產當天項目或是固定每分鐘檢查一次狀態或抄一次資料之類的。這邊示範每分鐘的00秒執行一次(hh:mm:00)。

            var (
                ticker         *time.Ticker
                duration       int64
                runCycleSecond int64
            )
            // 定義執行週期
            runCycleSecond = 60
            hour, minute, second := now.Clock()
            // 基礎時間
            nowTime := time.Date(2021, 10, 1, hour, minute, second, 0, locHongKong).Unix()
            // 下一分鐘開始執行
            setTime := time.Date(2021, 10, 1, hour, minute+1, 0, 0, locHongKong).Unix()
            minusTime := setTime - nowTime
    
            switch {
            case minusTime == 0:
                duration = runCycleSecond
            case minusTime > 0:
                // 餘數
                remainder, _ := decimal.NewFromFloat(float64(minusTime)).Mod(decimal.NewFromFloat(float64(runCycleSecond))).Float64()
                if remainder == 0 {
                    duration = runCycleSecond
                } else {
                    duration = int64(remainder)
                }
            }
    
            fmt.Println("每分鐘執行檢查, 下次檢查等待時間:", duration)
            ticker = time.NewTicker(time.Second * time.Duration(duration))
            for range ticker.C {
                Run()
                // 重置 timer = runCycleSecond
                ticker.Reset(time.Second * time.Duration(runCycleSecond))
            }
        }
    
        func Run() {
            fmt.Println("每分鐘檢查 > ", time.Now().Format("2006-01-02 15:04:05"))
        }
    
    
  • 執行結果

        每分鐘執行檢查, 下次檢查 等待時間: 4
        每分鐘檢查 >  2021-10-07 08:37:00
        每分鐘檢查 >  2021-10-07 08:38:00
        每分鐘檢查 >  2021-10-07 08:39:00
        每分鐘檢查 >  2021-10-07 08:40:00
    

這邊要注意ticker.Reset要使用go1.15以上才能編譯。


上一篇
day 28 - 請問, 有流程圖可以看嗎?
下一篇
day 30 - 結語
系列文
Let's Go! 解剖Go server開發到部署的過程30

尚未有邦友留言

立即登入留言