作者:程序员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")