Golang time模块常用示例
本文就不对官方文档做详细解析了,只贴一些常用的示例。如需查看官网点击上述链接即可。
一、时间的加减以及格式化示例:
func main() { s := time.Now().Add(time.Hour * -2) now := fmt.Sprintf("%04d-%02d-%02d %02d:%02d:%02d", s.Year(), s.Month(), s.Day(), s.Hour(), s.Minute(), s.Second()) fmt.Println(now) }
func Parse(layout, value string) (Time, error) func (t Time) Format(layout string) string
这两个函数前者用于从字符串中解析构造出Time对象,后者将Time对象重新格式化为想要的格式。
这其中最重要的是layout,layout是string类型的,类似于SQL中的YYYY-MM-DD这样的格式符。
time包自带的的layout有如下几种:
const ( Layout = "01/02 03:04:05PM '06 -0700" // The reference time, in numerical order. ANSIC = "Mon Jan _2 15:04:05 2006" UnixDate = "Mon Jan _2 15:04:05 MST 2006" RubyDate = "Mon Jan 02 15:04:05 -0700 2006" RFC822 = "02 Jan 06 15:04 MST" RFC822Z = "02 Jan 06 15:04 -0700" // RFC822 with numeric zone RFC850 = "Monday, 02-Jan-06 15:04:05 MST" RFC1123 = "Mon, 02 Jan 2006 15:04:05 MST" RFC1123Z = "Mon, 02 Jan 2006 15:04:05 -0700" // RFC1123 with numeric zone RFC3339 = "2006-01-02T15:04:05Z07:00" RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00" Kitchen = "3:04PM" // Handy time stamps. Stamp = "Jan _2 15:04:05" StampMilli = "Jan _2 15:04:05.000" StampMicro = "Jan _2 15:04:05.000000" StampNano = "Jan _2 15:04:05.000000000" )
如果想要自定义layout,那么只要使用这个固定的时间戳"2006-01-02T15:04:05Z07:00"进行拼接即可,示例如下:
func main() { mytime := "2011-02-03 00:00:00 +0000 UTC" t, err := time.Parse("2006-01-02 15:04:05 -0700 MST", mytime) fmt.Println(t, err) } // 2011-02-03 00:00:00 +0000 UTC <nil>
需要注意的是layout中的时间必须是这个2006年1月2号15:05:05,MST时区。
写其他的时间or时区可能会造成解析失败...
二、通过time.Date()和time.Parse(),time.Unix()快速构造time.Time类型
t := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC) t, err := time.Parse(time.RFC3339, "2009-10-23T12:04:05+08:00") // 此处需注意,虽然time.RFC3339展示的时区前缀为Z,但实际使用时使用+号才有效,使用Z会错误解析为0000-01-01 00:00:00 +0000 UTC t := time.Unix(<epoch time second>, 0) t := time.UnixMicro(<epoch time microsecond>) t := time.UnixMilli(<epoch time millisecond>) // epoch time即unix time
三、通过time.Since()快速的计算到当前的时间
time.Since(u time)可以实现时间的快速计算,得到一个可打印的、易于人眼识别的time.Duration类型。其相当于time.Now().Sub(t)。
t, _ = time.Parse(time.RFC3339, "2020-09-30T11:04:05+08:00") fmt.Println(time.Since(t)) // 打印结果为2h28m1.0416486s
四、时间乘法
time.Second/Hour等可以直接与数字相乘,但不能直接与数字类型的变量相乘.......这是一个很不合理、很反直觉的设计,目前只能接受,未来go团队可能改进。
变通办法为使用time.Duration将数字变量转化为一个time.Duration类型,这样就可以与time.Second相乘了。例如:
time.Since(startTime) > 24 * time.Hour * time.Duration(bc.KeepDays)
time.Second就是Duration类型,而time.Duration本质上是一个int64的数字。可以将time.Duration(数字)得到的结果看作时间计量值,乘以对应的时间单位后得到一个具体的时间长度。
五、有用的计时channel
很多时候我们需要为某个/多个任务计时,或者有每隔几秒打印日志等待任务执行完毕的需求。time包提供了实现此类需求的简易channel。
使用time.After()和time.NewTicker可以便捷的构造相关时间等待的channel和对象,接下来看两个简单示例:
func test(done chan<- bool) { time.Sleep(2 * time.Second) done <- true } func main() { timeOutSec := 5 timeOutChan := time.After(time.Second * time.Duration(timeOutSec)) // 开启一个goroutine,检测其是5秒否超时 testDoneChan := make(chan bool) go test(testDoneChan) select { case <- testDoneChan: fmt.Println("Test Done!") case <- timeOutChan: fmt.Println("Test Time Out!") return } } // 如果需要同时检测多个子任务,可以把他们的doneChan放入一个waitGroup中,然后开启一个单独的goroutine和chan等待wg.Wait()完毕后写入chan。之后通过select这个chan来判断所有任务是否都已执行完毕。
func test(done chan<- bool) { time.Sleep(10 * time.Second) done <- true } func main() { ticker := time.NewTicker(time.Second * 2) defer ticker.Stop() // 开启一个goroutine,每2秒tick一次直到test执行完 testDoneChan := make(chan bool) go test(testDoneChan) for { select { case <- testDoneChan: fmt.Println("Test Done!") return case t := <- ticker.C: fmt.Println("Current time:", t.Format(time.RFC3339)) } } }