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小时之后