Golang基礎(chǔ)筆記十一之日期與時(shí)間處理
本文首發(fā)于公眾號(hào):Hunter后端
本篇筆記介紹 Golang 里日期與時(shí)間的處理,以下是本篇筆記目錄:
- 當(dāng)前日期與時(shí)間的獲取
- 字符串與時(shí)間格式的互相轉(zhuǎn)換
- 時(shí)間戳與時(shí)間格式的互相轉(zhuǎn)換
- 日期與時(shí)間的加減
- 星期數(shù)的獲取
- 定時(shí)器與計(jì)時(shí)器
1、當(dāng)前日期與時(shí)間的獲取
在 Golang 里,日期和時(shí)間處理都通過 time 包來實(shí)現(xiàn)。
如果我們想獲取當(dāng)前時(shí)間,我們可以使用 time.Now() 來操作:
now := time.Now()
fmt.Println(now) // 2025-06-29 12:29:16.112605 +0800 CST m=+0.000154626
如果我們想獲取單獨(dú)的年月日時(shí)分秒字段,可以使用下面的操作:
now := time.Now()
year, month, day := now.Date()
hour, minute, second := now.Clock()
fmt.Println(year, month, day) // 2025 June 29
fmt.Println(hour, minute, second) // 15 29 50
可以看到輸出的月份是 June,但實(shí)際上月份是一個(gè)自定義的類型 Month,本質(zhì)上也是一個(gè) int 型數(shù)據(jù),其源代碼如下:
type Month int
const (
January Month = 1 + iota
February
March
April
May
June
July
August
September
October
November
December
)
所以,對(duì)于這里的年月日時(shí)分秒的單個(gè)變量,我們想將其組合輸出為一般的 %Y-%m-%d %H:%M:%S 格式,可以如下操作:
fmt.Printf("%d-%02d-%02d %02d:%02d:%02d\n", year, month, day, hour, minute, second)
我們也可以分別單獨(dú)獲取對(duì)應(yīng)的年月日時(shí)分秒數(shù)據(jù):
now := time.Now()
fmt.Println(now.Year())
fmt.Println(now.Month())
fmt.Println(now.Day())
fmt.Println(now.Hour())
fmt.Println(now.Minute())
fmt.Println(now.Second())
2、字符串與時(shí)間格式的互相轉(zhuǎn)換
1. 時(shí)間格式轉(zhuǎn)字符串
在其他計(jì)算機(jī)語言中,如果想將時(shí)間字段轉(zhuǎn)化為字符串,格式化的操作比如 Python,一般是類似于 %Y-%m-%d %H:%M:%S 這種,但是 Golang 里是一個(gè)特殊的格式化字段,為 2006-01-02 15:04:05。
Go 里對(duì)時(shí)間字段格式化的函數(shù)為 Format(),下面是將時(shí)間格式轉(zhuǎn)為字符串的操作為:
now := time.Now()
fmt.Println(now.Format("2006-01-02 15:04:05")) // 2025-06-29 22:45:11
當(dāng)然,格式化操作也可以單獨(dú)針對(duì)日期,或者時(shí)間,連接的符號(hào)也可以自定義:
now := time.Now()
fmt.Println(now.Format("2006/01/02")) // 2025/06/29
fmt.Println(now.Format("15:04:05")) // 23:22:06
2. 字符串轉(zhuǎn)時(shí)間格式
字符串轉(zhuǎn)時(shí)間格式使用 time.Parse() 函數(shù),以下是一個(gè)測試:
timeStr := "2025/06/29"
t, err := time.Parse("2006/01/02", timeStr)
if err != nil {
fmt.Println("str to time error: ", err)
} else {
fmt.Println("str to time is: ", t)
}
// str to time is: 2025-06-29 00:00:00 +0000 UTC
在這里 time.Parse() 返回兩個(gè)字段,一個(gè)是轉(zhuǎn)換后的時(shí)間字段,一個(gè)是轉(zhuǎn)換過程中的錯(cuò)誤。
上面是轉(zhuǎn)換日期,轉(zhuǎn)換時(shí)間也是一樣的操作:
timeStr = "20:24:24"
t, err := time.Parse("15:04:05", timeStr)
if err != nil {
fmt.Println("str to time error: ", err)
} else {
fmt.Println("str to time is: ", t)
}
// str to time is: 0000-01-01 20:24:24 +0000 UTC
而如果提供了錯(cuò)誤的時(shí)間字符串,返回的 err 字段則不會(huì)為空,比如下面這個(gè)示例:
timeStr := "2025/13/29"
t, err := time.Parse("2006/01/02", timeStr)
if err != nil {
fmt.Println("str to time error: ", err)
} else {
fmt.Println("str to time is: ", t)
}
// str to time error: parsing time "2025/13/29": month out of range
3、時(shí)間戳與時(shí)間格式的互相轉(zhuǎn)換
另一個(gè)在時(shí)間函數(shù)中常用到的用于轉(zhuǎn)換的數(shù)據(jù)是時(shí)間戳,下面介紹一下時(shí)間戳與時(shí)間的互相轉(zhuǎn)換。
1. 時(shí)間格式轉(zhuǎn)換為時(shí)間戳
下面是時(shí)間格式轉(zhuǎn)換為秒級(jí)的時(shí)間戳:
now := time.Now()
fmt.Println(now.Unix()) // 1751211429
還有轉(zhuǎn)換為毫秒,納秒級(jí)的操作:
now := time.Now()
fmt.Println(now.UnixMilli()) // 1751211522339
fmt.Println(now.UnixNano()) // 1751211522339955000
2. 時(shí)間戳轉(zhuǎn)換為時(shí)間格式
時(shí)間戳轉(zhuǎn)換為時(shí)間格式的函數(shù)為 time.Unix():
timestamp := 1751211429
targetTime := time.Unix(int64(timestamp), 0)
fmt.Println(targetTime) // 2025-06-29 23:37:09 +0800 CST
這里需要注意,輸入的時(shí)間戳需要是 int64 類型,輸入的第二個(gè)參數(shù)為納秒值,如果不需要那么精細(xì)的話,傳值為 0 即可。
4、日期與時(shí)間的加減
介紹日期與時(shí)間的加減,這里分為兩部分來介紹,一部分是時(shí)分秒,一部分是年月日,他們分別用到的函數(shù)是 Add() 和 AddDate()。
1. Add()-時(shí)分秒的加減
time.Duration
Add() 函數(shù)的參數(shù)類型是 time.Duration,Duration 也是自定義的一個(gè)時(shí)間單位,一納秒就是一個(gè) Duration,它的類型是 int64,范圍是:
const (
minDuration Duration = -1 << 63
maxDuration Duration = 1<<63 - 1
)
而時(shí)分秒也都根據(jù)其轉(zhuǎn)換關(guān)系定義好了各自的字段:
const (
Nanosecond Duration = 1
Microsecond = 1000 * Nanosecond
Millisecond = 1000 * Microsecond
Second = 1000 * Millisecond
Minute = 60 * Second
Hour = 60 * Minute
)
所以我們?cè)谑褂?Add() 函數(shù)的時(shí)候可以直接使用對(duì)應(yīng)的單位。
時(shí)分秒的加減
now := time.Now()
threeHoursLater := now.Add(3 * time.Hour)
thirtyMinutesAgo := now.Add(-30 * time.Minute)
twoDaysLater := now.Add(48 * time.Hour)
fmt.Println("now is: ", now.Format("2006-01-02 15:04:05"))
fmt.Println("three hours later is: ", threeHoursLater.Format("2006-01-02 15:04:05"))
fmt.Println("thirty minutes ago is: ", thirtyMinutesAgo.Format("2006-01-02 15:04:05"))
fmt.Println("two days later is: ", twoDaysLater.Format("2006-01-02 15:04:05"))
輸出結(jié)果如下:
now is: 2025-06-30 22:58:38
three hours later is: 2025-07-01 01:58:38
thirty minutes ago is: 2025-06-30 22:28:38
two days later is: 2025-07-02 22:58:38
2. AddDate()-年月日的加減
AddDate() 函數(shù)接收三個(gè)參數(shù),分別是 years、months、days,表示需要在當(dāng)前時(shí)間需要增加的年數(shù)、月數(shù)和天數(shù)。
如果是想要回溯過去的日期,在對(duì)應(yīng)的參數(shù)前加上負(fù)號(hào) - 即可。
如果是不需要指定的參數(shù)設(shè)為 0 即可。
比如想要獲取今天前一個(gè)月的日期,以及今天往后三天的日期,可以如下操作:
now := time.Now()
lastMonth := now.AddDate(0, -1, 0)
latestThreeDays := now.AddDate(0, 0, 3)
fmt.Println("last month is: ", lastMonth.Format("2006-01-02 15:04:05"))
fmt.Println("latest three days is: ", latestThreeDays.Format("2006-01-02 15:04:05"))
3. Add() 和 AddDate() 使用示例
這里分別使用 Add() 和 AddDate() 兩個(gè)函數(shù)打印出之后七天的日期,其操作如下:
使用 Add() 函數(shù):
now := time.Now()
for i := range 7 {
targetDate := now.Add(time.Duration(24*(i+1)) * time.Hour)
fmt.Println(targetDate.Format("2006-01-02"))
}
使用 AddDate() 函數(shù):
now := time.Now()
for i := range 7 {
targetDate := now.AddDate(0, 0, i+1)
fmt.Println(targetDate.Format("2006-01-02"))
}
4. 兩個(gè)時(shí)間點(diǎn)的差值
1) Sub()
如果我們想獲取兩個(gè)時(shí)間點(diǎn)之間差值,比如用于測試某個(gè)函數(shù)執(zhí)行的時(shí)間,可以使用 Sub() 函數(shù),返回的結(jié)果也是 time.Duration:
t1 := time.Now()
time.Sleep(3 * time.Second)
t2 := time.Now()
subResult := t2.Sub(t1)
fmt.Println(subResult) // 3.00144925s
我們可以將 subResult 直接轉(zhuǎn)換成我們想要的單位,比如毫秒和分鐘:
fmt.Println("use millseconds: ", subResult.Milliseconds()) // use millseconds: 3001
fmt.Println("use minutes: ", subResult.Minutes()) // use minutes: 0.05002786111666667
2) time.Since()
如果我們想獲取現(xiàn)在距離某個(gè)時(shí)間點(diǎn)的差值,也可以直接使用 time.Since() 函數(shù),其使用示例如下:
t1 := time.Now()
time.Sleep(2 * time.Second)
result := time.Since(t1)
fmt.Println(result) // 2.001462166s
5、星期數(shù)的獲取
我們也可以根據(jù)日期來獲取對(duì)應(yīng)的星期數(shù),比如今天是星期幾:
now := time.Now()
fmt.Println(now.Weekday()) // Monday
輸出的信息是 Monday。
星期數(shù)的底層數(shù)據(jù)也是 int 型,但是 Golang 將其包了一層,自定義了一個(gè) Weekday 的類型:
type Weekday int
const (
Sunday Weekday = iota
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
)
從周日開始到周六,分別是從 0 到 6,我們可以打印一下:
now := time.Now()
fmt.Printf("%s, %d\n", now.Weekday(), now.Weekday()) // Monday, 1
6、定時(shí)器與計(jì)時(shí)器
下面介紹一下 Golang 里 time 模塊的定時(shí)器和計(jì)時(shí)器如何使用。
1. 定時(shí)器
定時(shí)器有兩個(gè)寫法,一個(gè)是 time.NewTimer(),一個(gè)是 time.After(),接收的參數(shù)類型都是 time.Duration。
下面直接用代碼示例來介紹如何使用。
1) time.NewTimer
有一個(gè)需求,我們需要調(diào)用某個(gè)函數(shù),但是函數(shù)的執(zhí)行時(shí)長是不定的,而整體執(zhí)行的時(shí)長是有限的,我們希望能在指定的時(shí)間內(nèi)返回?cái)?shù)據(jù),如果這個(gè)函數(shù)執(zhí)行超時(shí)就不希望它再執(zhí)行了,能夠立即獲取其超時(shí)狀態(tài)。
針對(duì)這個(gè)需求,我們就可以使用定時(shí)器來完成。
首先,我們有一個(gè)需要執(zhí)行的函數(shù),這個(gè)函數(shù)可能是調(diào)用某個(gè)接口,可能是從 Redis 或者 MySQL 中讀數(shù)據(jù),但是其執(zhí)行時(shí)長是不定的,我們用 RandomTimeWork() 函數(shù)來替代,并且在其中設(shè)置一個(gè)隨機(jī)休息的時(shí)間用來模擬不定的執(zhí)行時(shí)長:
func RandomTimeWork() int {
sleepSeconds := rand.Intn(10)
time.Sleep(time.Duration(sleepSeconds) * time.Second)
return sleepSeconds
}
然后我們需要一個(gè)中間函數(shù)使用通道來傳遞其返回值:
func CallFunc(ch chan int) {
result := RandomTimeWork()
ch <- result
}
接下來就是主函數(shù)的操作,我們需要先設(shè)置一個(gè)定時(shí)器,這里我們?cè)O(shè)置為 5 秒的超時(shí):
timeout := time.NewTimer(5 * time.Second)
然后設(shè)置一個(gè)通道用于傳輸數(shù)據(jù),并且使用 goroutine 來調(diào)用:
ch := make(chan int)
go CallFunc(ch)
最后就是使用 select 操作來進(jìn)行等待,看是通道先返回?cái)?shù)據(jù),還是定時(shí)器先計(jì)時(shí)完畢:
select {
case result := <-ch:
fmt.Println("call func success, sleep seconds: ", result)
case <-timeout.C:
fmt.Println("call func timeout")
}
其整體代碼如下:
package main
import (
"fmt"
"math/rand"
"time"
)
func RandomTimeWork() int {
sleepSeconds := rand.Intn(10)
time.Sleep(time.Duration(sleepSeconds) * time.Second)
return sleepSeconds
}
func CallFunc(ch chan int) {
result := RandomTimeWork()
ch <- result
}
func main() {
timeout := time.NewTimer(5 * time.Second)
ch := make(chan int)
go CallFunc(ch)
select {
case result := <-ch:
fmt.Println("call func success, sleep seconds: ", result)
case <-timeout.C:
fmt.Println("call func timeout")
}
}
在上面的操作中,如果待執(zhí)行的函數(shù)先執(zhí)行完畢,而定時(shí)器卻沒有結(jié)束,我們可以手動(dòng)執(zhí)行停止定時(shí)器操作。
實(shí)際上,如果不手動(dòng)停止,默認(rèn)等待定時(shí)器觸發(fā)結(jié)束或者程序完畢也可以,但是在高并發(fā)場景下,如果有很多未完成的定時(shí)器會(huì)造成內(nèi)存占用增加,且增加程序的 GC 負(fù)擔(dān),因此,我們可以選擇手動(dòng)提前停止定時(shí)器。
停止操作也很簡單,在獲取到函數(shù)執(zhí)行的結(jié)果后,我們可以如下操作:
select {
case result := <-ch:
fmt.Println("call func success, sleep seconds: ", result)
timeout.Stop()
case <-timeout.C:
fmt.Println("call func timeout")
}
2) time.After
除了 time.NewTimer(),我們可以使用更簡單的 time.After() 函數(shù)來執(zhí)行一個(gè)定時(shí)器,相對(duì)上面的完整示例,我們只改動(dòng) main 函數(shù)里代碼如下:
func main() {
ch := make(chan int)
go CallFunc(ch)
select {
case result := <-ch:
fmt.Println("call func success, sleep seconds: ", result)
case <-time.After(5 * time.Second):
fmt.Println("call func timeout")
}
}
注意:使用 time.After() 有個(gè)問題就是不可以提前手動(dòng)結(jié)束定時(shí)器。
2. 計(jì)時(shí)器
計(jì)時(shí)器常用于定時(shí)任務(wù),比如每隔多長時(shí)間執(zhí)行某個(gè)動(dòng)作,用到的函數(shù)是 time.NewTicker,傳入的參數(shù)是 time.Duration。
比如我們想將某個(gè)函數(shù)設(shè)置為每隔三秒鐘執(zhí)行一次,我們可以如下操作:
func TargetFunc() {
fmt.Println("call target func at: ", time.Now().Format("2006-01-02 15:04:05"))
}
func CallFuncEntrance(ticker *time.Ticker) {
// for t := range ticker.C {
for range ticker.C {
TargetFunc()
}
}
func main() {
ticker := time.NewTicker(3 * time.Second)
go CallFuncEntrance(ticker)
time.Sleep(10 * time.Second)
ticker.Stop()
}
上面的示例中,開啟了一個(gè) goroutine 并將計(jì)時(shí)器作為參數(shù)傳入,每隔三秒鐘觸發(fā)一次目標(biāo)函數(shù) TargetFunc()。
并且在最后執(zhí)行了計(jì)時(shí)器的停止操作 ticker.Stop()。
在這里如果我們想重置計(jì)時(shí)器的間隔時(shí)間,可以使用 Reset() 操作:
func main() {
ticker := time.NewTicker(3 * time.Second)
go CallFuncEntrance(ticker)
time.Sleep(10 * time.Second)
ticker.Reset(1 * time.Second)
time.Sleep(4 * time.Second)
ticker.Stop()
}
執(zhí)行 main 函數(shù)可以看到目標(biāo)函數(shù)執(zhí)行的時(shí)間間隔會(huì)從 3s 變成 1s。
定時(shí)任務(wù)的其他實(shí)現(xiàn)方式
除了這個(gè)操作用來執(zhí)行定時(shí)任務(wù)外,我們還可以使用 for{} 和 time.Sleep() 操作來實(shí)現(xiàn)定時(shí)任務(wù),其示例如下:
func TargetFunc() {
fmt.Println("call target func at: ", time.Now().Format("2006-01-02 15:04:05"))
}
func CallFuncEntrance() {
for {
TargetFunc()
time.Sleep(3 * time.Second)
}
}
func main() {
go CallFuncEntrance()
time.Sleep(10 * time.Second)
}

浙公網(wǎng)安備 33010602011771號(hào)