一文搞懂go时间库time的使用

作者:程序员CKeen
博客:http://ckeen.cn

长期坚持做有价值的事!积累沉淀,持续成长,升维思考!希望把编码作为长期兴趣爱好😄


时间和日期是我们日常开发中经常会用到的功能。Go 语言中的 time 包提供了处理时间和日期的功能。它包含了许多函数和类型,用于获取当前时间、格式化时间、解析字符串为时间、进行时间计算等操作。

1. 时间类型

go中使用time.Time类型来定义时间类型。 可以通过Now方法获取当前时间的对象

// 获取当前时间
time1 := time.Now()
fmt.Printf("time now:%+v,  string:%s\n", time1, time1.String())

获取到时间对象后,我们就能根据对象获取具体的日期、时间及星期等信息

fmt.Printf("Time: %d-%d-%d %d:%d:%d %s\n", time1.Year(),time1.Month(),time1.Day(),time1.Hour(),
		time1.Minute(),time1.Second(), time1.Weekday().String())
// 打印结果:Time:2023-7-11 14:47:23 Tuesday

fmt.Printf("days:%d\n", time1.YearDay())
// 打印结果:days:192, 返回今天在1年中的天数

除了直接获取当前时间,time库还提供了一个time.Date的方式,根据日期、时间及区域来创建Time实例,如下:

time11 := time.Date(2023,7,10,15,10,11,999000111,time.Local)
// 依次传入的参数为: 年、月、日、时、分、秒、纳秒,时间Location
fmt.Printf("Time:%d-%d-%d %d:%d:%d %s\n", time11.Year(),
time11.Month(),time11.Day(),time11.Hour(),
time11.Minute(),time11.Second(), time11.Weekday().String())

// 打印结果:Time:2023-7-10 15:10:11 Monday

2. 时间戳

时间戳是自1970年1月1日(08:00:00GMT)至当前时间的总毫秒数。它也被称为Unix时间戳(UnixTimestamp)。

时间戳一般有精确到不同秒数的时间戳,比如精确到秒、毫秒、微秒和纳秒等,不同精度的长度也不一样(1秒=1000毫秒=1000000微秒=1000000000纳秒)。

我们来具体看下获取时间戳的方式

time1 := time.Now()
fmt.Printf("Timestamp: 秒unix:%d,纳秒unixNano:%d,微秒unixMicro:%d,毫秒unixMilli:%d\n", time1.Unix(), time1.UnixNano(), time1.UnixMicro(),time1.UnixMilli())

// 打印结果:Timestamp: 秒unix:1689060513,纳秒unixNano:1689060513736593000,微秒unixMicro:1689060513736593,毫秒unixMilli:1689060513736

从打印结果可以看到不同精度下的时间戳。除了可以从时间Time对象从很方便的获取不同的时间戳对象,同时我们也可以通过时间戳来构建Time对象实例,go的time库提供了如下几个函数来构建Time实例:

func Unix(sec int64, nsec int64) Time  
func UnixMilli(msec int64) Time
func UnixMicro(usec int64) Time

我们来具体看下操作的方法

// 首先我们获取2023-07-11 00:00:00 时间戳为:1689004800

seconds_time := time.Unix(int64(1689004800),int64(9999999999))
fmt.Printf("seconds time:%s\n",seconds_time.String())
// 打印结果: seconds time:2023-07-11 00:00:09.999999999 +0800 CST

milli_seconds_time := time.UnixMilli(int64(1689004800999))
fmt.Printf("milli secords time:%s\n", milli_seconds_time.String())
// 打印结果:milli secords time:2023-07-11 00:00:00.999 +0800 CST

micro_seconds_time := time.UnixMicro(int64(1689004800999999))
fmt.Printf("micro seconds time:%s\n", micro_seconds_time.String())
// 打印结果:micro seconds time:2023-07-11 00:00:00.999999 +0800 CST
  • 第一个方法Unix(sec int64, nsec int64)通过传入时间戳秒和纳秒两个参数来构建时间对象,可以看到他打印的除了包含日期、时间外,最后还有个9位纳秒的数值:999999999
  • 第二个方法UnixMilli(msec int64)通过传入微秒的时间戳来构建时间对象,可以看到他打印的除了包含日期、时间外,最后显示的值为3位的微秒数值:999
  • 第三个方法UnixMicro(usec int64)通过传入毫秒的时间戳来构建时间对象,可以看到他打印的除了包含日期、时间外,最后显示的值为6位的毫秒数值:999999

3. 时间间隔Duration

time.Duration是time包定义的一个类型,它代表两个时间点之间经过的时间,以纳秒为单位。time.Duration表示一段时间间隔,可表示的最长时间段大约290年。

time包中定义的一些常量来表示具体的时间单位到纳秒换算

const (
	Nanosecond  Duration = 1
	Microsecond          = 1000 * Nanosecond
	Millisecond          = 1000 * Microsecond
	Second               = 1000 * Millisecond
	Minute               = 60 * Second
	Hour                 = 60 * Minute
)

下面我们具体看下Duration的创建和对时间的换算

// 创建Duration实例
duration := time.Duration(1000 * time.Second)  
fmt.Printf("duration:%+v, int64:%d\n", duration, duration.Nanoseconds())
// 打印结果:duration:16m40s, int64:1000000000000

// 将字符串转换为Duration
duration1, err := time.ParseDuration("16m40s")
if err == nil {
  fmt.Printf("duration:%+v, int64:%d\n", duration1, duration1.Nanoseconds())
}
// 打印结果:duration:16m40s, int64:1000000000000

// duration到小时,分,秒的转换
fmt.Printf("hour:%f,minutes:%f, seconds:%f\n",duration.Hours(), duration.Minutes(), duration.Seconds())
// 打印结果: hour:0.277778,minutes:16.666667, seconds:1000.000000

除了使用Duration对间隔时间的换算,还能和Time的时间对象进行运算,在后面的“时间操作”部分我们将详细的介绍

4. 时间操作

4.1 时间(Time)+ 时间间隔(Duration)的计算的函数Add,函数如下:

  func (t Time) Add(d Duration) Time

实际操作的例子,比如我们计算当前时间72小时前的时间

 time72hour := time.Now().Add( -72 * time.Hour )
 fmt.Printf(" before 72 hours time:%s\n", time72hour)
 
 // 打印结果: before 72 hours time:2023-07-08 22:01:11.023477 +0800 CST m=-259199.999698469

4.2 时间(Time) + 日期间隔(Year,Month, Day)的计算的函数AddDate,函数如下:

func (t Time) AddDate(years int, months int, days int) Time

该方法平时我们在时间计算上用的更多,比如我们计算1年前,或者下个月的今天等

now := time.Now()

// 计算1年前的今天
time1year := now.AddDate(-1,0, 0)
fmt.Printf(" before one year time:%s\n", time1year)
// 打印结果: before one year time:2022-07-11 22:08:00.335251 +0800 CST

// 计算下个月的今天
time1month := now.AddDate(0, 1,0)
fmt.Printf(" after one month time:%s\n", time1month)
// 打印结果:after one month time:2023-08-11 22:08:00.335251 +0800 CST

4.3 计算两个时间直接的差值函数Sub,函数如下:

func (t Time) Sub(u Time) Duration

使用示例:

start := time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC)
end := time.Date(2023, 7, 11, 12, 0, 0, 0, time.UTC)

sub := end.Sub(start)
fmt.Printf("sub = %v\n", sub)
// sub = 4596h0m0s

4.4 时间的比较,提供一下三个函数

// 比较两个时间是否相等,即使他们的时区不同,也可以相等
func (t Time) Equal(u Time) bool
// 判断当前实例时间是否在某个时间之前
func (t Time) Before(u Time) bool
// 判断当前实例时间是否在某个时间之后
func (t Time) After(u Time) bool

​ 有了以上这些对时间操作的函数,我们可以很方便的实现一些时间计算的求解,比如类似获取上一个月的第一天和最后一天等需求,实现方式如下:

now := time.Now()
// 计算上个月的第一天
lastMonthFirstDay := now.AddDate(0, -1,-now.Day() + 1)
// 计算上个月的最后一天
lastMonthLastDay := now.AddDate(0, 0, -now.Day())
fmt.Println(lastMonthFirstDay)
fmt.Println(lastMonthLastDay)

//打印结果:2023-06-01 22:31:41.323683 +0800 CST
//打印结果:2023-06-30 22:31:41.323683 +0800 CST

5. 时间格式化

在的其他的语言中我们一般以"yyyy-MM-dd HH:mm:ss"的之类的格式,来对时间进行字符串的格式化,而go中时间格式化又是怎么样的呢? 下面我们来看个实例:

time.Now().Format("2006-01-02 15:04:05")

这格式化的方法看起来是不是有点奇怪,我们查看go的时间time包,可以看到时间格式化的Layout参数:

Layout      = "01/02 03:04:05PM '06 -0700" 

这个表示用英文的时间表达形式,表示1月2号, 下午3点4分过5秒,06年的07时区,所以连起来就是01 02 03 04 05 06 07。其中下午3点,在24小时制里面是15点,所以使用24小时制格式化的时候使用15点。

time包提供了Format函数将Time格式化为字符串,同时也提供了将时间字符串转为Time实例的函数,函数如下:

func Parse(layout, value string) (Time, error)
func ParseInLocation(layout, value string, loc *Location) (Time, error)

这两个方法带的参数不同,那么他们有啥区别的,首先我们实际操作看一下:

timeParse,err := time.Parse(timerFormat,"2023-07-10 10:10:10")
if err == nil {
	fmt.Printf("time string:%s\n", timeParse)
}
// 	打印结果:time string:2023-07-10 10:10:10 +0000 UTC


timeParseLocation,err1 := time.ParseInLocation(timerFormat,"2023-07-10 10:10:10",time.Local)
if err1 == nil {
	fmt.Printf("time string:%s\n", timeParseLocation.String())
}
// 打印结果:time string:2023-07-10 10:10:10 +0800 CST

从实例结果可以看出来,他们时间都是一样的,但是时区是不一样的, Parse方法没有穿Location参数,它使用的是默认的UTC时区,而第二方法我传入的time.Local的Location参数,它使用的是我当前时区东8区的时区。那么时区对这个时间有没有影响呢,我们从下面的例子中可以得出结论:

// timeParse和timeParseLocation是上面例子中的变量

fmt.Println(timeParse.Equal(timeParseLocation))
fmt.Println(timeParse.After(timeParseLocation))
fmt.Println(timeParse.Sub(timeParseLocation))

// 打印结果:
// false
// true
// 8h0m0s
	

根据打印的结果我们可以看到,UTC的时间比当前东八区的时间要晚,并且晚了8小时。所以我们在使用字符串转换的时候,需要注意时区的问题。

6. 时间Time中的Location

Location将时间映射到当时正在使用的区域。通常,Location表示在一个地理区域中使用的时间偏移量的集合

在time.Time的结构中,有一个就是Location结构的指针用来定义当前时间的时区信息,time.Time结构定义:

type Time struct {
	wall uint64
	ext  int64
	loc *Location
}

time库中预定义的time.UTC定义的UTC时区Locaiton,还有time.Local定义的本地时区Locatoin。除了time预定外的两个时区,time提供了LoadLocation函数来获取*Location实例,示例如下:

loc,_ := time.LoadLocation("UTC")
loc1,_ := time.LoadLocation("Asia/Shanghai")
loc2,_ := time.LoadLocation("Europe/London")
loc3,_ := time.LoadLocation("Asia/Tokyo")
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序猿CKeen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值