Time
go version:go1.17.2
- 提供了获取系统时间、时间计算、比较、等一系列操作
1. 当前时间获取
func timeDemo() {
now := time.Now() //当前时间
fmt.Println("now:", now)
year := now.Year()
month := now.Month()
day := now.Day()
hour := now.Hour()
minute := now.Minute()
second := now.Second()
fmt.Printf("%d-%02d-%02d %02d:%02d:%02d\n", year, month, day, hour, minute, second)
utc := now.UTC()
fmt.Println("utc:", utc)
}
//结果
now: 2022-09-23 16:39:52.2534551 +0800 CST m=+0.001630501
2022-09-23 16:39:52
utc: 2022-09-23 08:39:52.2534551 +0000 UTC
- 注:UTC,在互联网通信中,同一使用一个标准时间,称为协调时(UTC,Universal Time Coordinated)。UTC与格林威治时间(GMT,Greenwich Mean TIme)一样,都与英国伦敦的本地时相同。
- 换算:本地时间 = UTC+时区差
- 北京市区是东八区,领先UTC 8小时,例如上面utc 2022-09-23 08:39:52.2534551,那么北京时间就是 2022-09-23 16:39:52.2534551(now和utc后面的+XXXX 就是时区偏差)
- 地球一个有24个时区,相邻时区之间差一个小时
- CST:Central Standard Time, 中国 :UTC+8
2. 时间戳
- 时间戳是自1970年1月1日(08:00:00GMT)至当前时间的总毫秒数。它也被称为Unix时间戳 (UnixTimestamp)。
func timestampDemo() { //时间戳
now := time.Now()
timestamp1 := now.Unix() //时间戳 默认秒
timestamp2 := now.UnixNano() //时间戳纳秒 同样地有微秒获取:UnixMicro、毫秒:UnixMilli
fmt.Printf("timestamp1:%vs \n", timestamp1)
fmt.Printf("timestamp2:%vns \n", timestamp2)
}
//结果
timestamp1:1663925651s
timestamp2:1663925651973428500ns
- time.Unix()函数可以将时间戳转为时间格式(time.Time)。
- Unix(sec int64, nsec int64) Time
- Unix() 返回自1970年1月1日UTC以来与给定Unix时间、通过输入sec秒和nsec纳秒为转换前的时间戳。传递超出范围[0,999999999]的nsec是有效的。并非所有秒值都有相应的时间值,值1<<63-1除外(int64的最大值)。
func timestampConvDemo() {
var timestamp int64
timestamp = 1663925675
timeObj := time.Unix(timestamp, 0)
fmt.Println(timeObj)
year := timeObj.Year()
month := timeObj.Month()
day := timeObj.Day()
hour := timeObj.Hour()
minute := timeObj.Minute()
second := timeObj.Second()
fmt.Printf("%d-%02d-%02d %02d:%02d:%02d\n", year, month, day, hour, minute, second)
}
//结果
2022-09-23 17:34:35 +0800 CST
2022-09-23 17:34:35
3. 时间间隔
-
time.Duration
-
Duration以int64类型计数两个瞬时之间经过的纳秒数。可表示的最长时间段大约290年
-
time包中定义的时间间隔类型常量如下:
const (
Nanosecond Duration = 1
Microsecond = 1000 * Nanosecond
Millisecond = 1000 * Microsecond
Second = 1000 * Millisecond
Minute = 60 * Second
Hour = 60 * Minute
)
- 例如:time.Hour 表示1小时、time.Minute表示1分钟
func timeDurationDemo() {
fmt.Println("1 Hour:", 1*time.Hour)
fmt.Println("2 Minute:", 2*time.Minute)
}
//结果
1 Hour: 1h0m0s
2 Minute: 2m0s
//Duration实现了String方法,直接println的时候会调用string方法,所以可以输出格式化的时间
4. 时间操作
4.1 date
- 自定义时间
- 参数:年、月、日、时、分、秒、毫秒+时区
- 返回:时间对象
func Date(year int, month Month, day, hour, min, sec, nsec int, loc *Location) Time
func funcDateDemo() {
t := time.Date(2022, 9, 26, 12, 14, 0, 0, time.UTC)
u := time.Date(2022, 9, 25, 36, 14, 0, 0, time.UTC)
fmt.Println("t:", t)
fmt.Println("u:", u)
}
//结果
t: 2022-09-26 12:14:00 +0000 UTC
u: 2022-09-26 12:14:00 +0000 UTC
//注意上面u中 hour = 36 = 1day +12hour
4.2 add
- 我们在日常的编码过程中可能会遇到要求时间+时间间隔的需求,Go语言的时间对象有提供add方法如下
func (t time) Add(d Duration) Time
func funcAddDemo() {
now := time.Now()
later := now.Add(1 * time.Hour) //当前时间加1小时后的时间
fmt.Println("now :", now)
fmt.Println("later:", later)
}
//结果
now : 2022-09-26 10:20:54.9062954 +0800 CST m=+0.001528201
later: 2022-09-26 11:20:54.9062954 +0800 CST m=+3600.001528201
4.3 sub
- 求两个时间之间的差值(并非求某个时间点的某些时间段之前的时间)
- 返回一个时间段 t - u。如果结果超出了Duration可以表示的最大值/最小值,将返回最大值/最小值。要获取时间点t-d(d为Duration),可以使用t.ADD(-d)
func (t Time) Sub(u Time) Duration
func funcSubDemo() {
now := time.Now()
later := now.Add(1 * time.Hour) //当前时间加1小时后的时间
pre := now.Sub(later) //两时间点差值,now-later
fmt.Println("now:", now)
fmt.Println("pre:", pre)
}
//结果
now: 2022-09-26 10:37:21.5443079 +0800 CST m=+0.001583901
pre: -1h0m0s
4.4 equal
- 判断两个时间是否相同,会考虑时区的影响,因此不同时区标准的时间也可以正确比较。本方法和用t==u不同,这种方法还会比较地点和时区信息。
func (t Time) Equal(u Time) bool
func funcEqualDemo() {
now := time.Now()
later := now.Add(1 * time.Hour)
fmt.Println("now :", now)
fmt.Println("later:", later)
fmt.Println("check:", now.Equal(later))
}
//结果
now : 2022-09-26 10:43:31.4666713 +0800 CST m=+0.001551401
later: 2022-09-26 11:43:31.4666713 +0800 CST m=+3600.001551401
check: false
4.5 before
- 如果t代表的时间点在u之后,返回true;否则返回false
func (t Time) Before(u Time) bool
4.6 after
- 如果t代表的时间点在u之前,返回true;否则返回false
func (t Time) After(u Time) bool
5. 时间格式化
- 时间类型有一个自带方法Format进行格式化,需要注意的是Go语言中格式化时间模板不是常见的Y-M-D H:M:S 而是使用Go的诞生时间2006年1月2日15点04分(记忆口诀为2006 1 2 3 4 )。
- 补充:如果想格式化为12小时方式,需指定PM。
func (t Time) Format(layout string) string
func funcFormatDemo() {
now := time.Now()
fmt.Println("now:", now)
//格式化的模板为Go的出生时间2006年1月2号15点04分 Mon Jan
//24小时制
fmt.Println(now.Format("2006-01-02 15:04:05:000 Mon Jan"))
//12小时制
fmt.Println(now.Format("2006-01-02 15:04:05:000 PM Mon Jan"))
fmt.Println(now.Format("2006/01/02 15:04"))
fmt.Println(now.Format("2006/01/02 15:04:05"))
fmt.Println(now.Format("15:04 2006/01/02"))
fmt.Println(now.Format("2006/01/02"))
}
//结果
now: 2022-09-26 13:46:04.4665871 +0800 CST m=+0.001843601
2022-09-26 13:46:04:000 Mon Sep
2022-09-26 13:46:04:000 PM Mon Sep
2022/09/26 13:46
2022/09/26 13:46:04
13:46 2022/09/26
2022/09/26
- 解析字符串格式的时间 ParseInLocation
//加载时区
func LoadLocation(name string) (*Location, error)
//按照指定时区(loc)和指定格式(layout)解析字符串时间(value)
func ParseInLocation(layout, value string, loc *Location) (Time, error)
func funcParseDemo() {
now := time.Now()
fmt.Println(now)
//加载时区
loc, err := time.LoadLocation("Asia/Shanghai")
if err != nil {
fmt.Println(err)
return
}
//按照指定时区和指定格式解析字符串时间
timeObj, err := time.ParseInLocation("2006/01/02 15:04:05", "2022/09/26 13:40:00", loc)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(timeObj)
fmt.Println(timeObj.Sub(now))
}
//结果
2022-09-26 13:40:10.5830133 +0800 CST m=+0.001559201
2022-09-26 13:40:00 +0800 CST
-10.5830133s
6. 定时器
- time包中提供了方法,用来实现定时器
6.1 NewTimer
- NewTimer创建一个新计时器,该计时器将在一段时间后(d)在其通道上发送当前时间。(*Timer中包含了一个单向只读通道 Time)
func NewTimer(d Duration) *Timer
type Timer struct {
C <-chan Time
r runtimeTimer
}
func funcNewTimerDemo() {
timer := time.NewTimer(1 * time.Second) //1s定时器
defer timer.Stop()
var count int
for {
select {
case <-timer.C: //监听timer通道,d 时间间隔后触发
count++
fmt.Println("process")
timer.Reset(1 * time.Second) //需要重置定时器
if count == 5 {
return
}
}
}
}
//结果 每隔1s输出一个process,5S后结束
process
process
process
process
process
6.2 NewTicker
- NewTicker创建一个新计时器,该计时器将在一段时间后(d)在其通道上发送当前时间。(*Ticker中包含了一个单向只读通道 Ticker)
- 周期由d决定
- Ticker会自动调整周期,以你不接收速度慢的问题
- 时间间隔d必须大于0,否则将会产生panic
- 停止ticker会释放相关资源
- 每次触发ticker后,不需要reset
func NewTicker(d Duration) *Ticker
func funcTickerDemo() {
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()
var count int
for {
select {
case <-ticker.C: //监听timer通道,d 时间间隔后触发
count++
fmt.Println("process")
if count == 5 {
return
}
}
}
}
//结果 每隔1s输出一个process,5S后结束
process
process
process
process
process
6.3 After
- 实际调用的就是NewTimer,返回NewTimer.C。(NewTimer.C是一个单向通道)
func After(d Duration) <-chan Time {
return NewTimer(d).C
}
func funcAfterDemo() {
var count int
for {
select {
case <-time.After(1 * time.Second):
count++
fmt.Println("process")
if count == 5 {
return
}
}
}
}
//结果 每隔1s输出一个process,5S后结束
process
process
process
process
process
//这里没有reset也能实现周期触发的原因是for select结构每次循环都会重新评估case中的表达式,向timer中装载了值。
//所以可以通过After实现超时判定机制:
// for {
// select {
// case <-time.After(30 * time.Second): //ch 持续30s没有数据将触发超时机制
// fmt.Println("overtime!")
// //处理超时后果
// return
// case <-ch:
// fmt.Println("process!")
// }
// }
7. 时间点判定
-
需求:每日15点启动发邮件流程,给指定人员发送邮件。(15.00-16.00都可)
-
思路:获取当天早15点时间对象,获取当前时间对象,做差值,判断时间间隔是否在给定时间范围内,如果在范围内,发送邮件。
func funcSendEmailDemo() {
timer := time.NewTimer(1 * time.Second)
defer timer.Stop()
for {
select {
case <-timer.C:
//取当天0点时间
t := time.Now()
todayZeroTime := time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location())
//计算当天15点时间
offsetTime, _ := time.ParseDuration("+15h")
tObj := todayZeroTime.Add(offsetTime)
fmt.Println("当天十五点:", tObj)
fmt.Println("当前时间 :", t)
//做时间差值
tDif := t.Sub(tObj)
timer.Reset(1 * time.Second) //定时器重置
if tDif >= time.Duration(0*time.Hour) && tDif <= time.Duration(1*time.Hour) { //判定是否在15.00-16.00区间
timer.Reset(24 * time.Hour) //定时器重置 24H后再判定
fmt.Println("发送邮件流程启动,tDif:", tDif)
//发送邮件
}
}
}
}
//结果
当天十五点: 2022-09-26 15:00:00 +0800 CST
当前时间 : 2022-09-26 15:27:31.3255758 +0800 CST m=+1.009391101
发送邮件流程启动,tDif: 27m31.3255758s
//下次判定将在24小时之后