常见概念
- GMT和UTC时间:GMT格林尼治时间,作为世界标准时间,UTC与GMT误差非常小,可以认为是一致的, 圈子里都以UTC时间作为标准时间;
- 时区:英国为0时区,中国在东八区 (UTC+08),UTC时间加上8小时就是我们本地时间了 有时会看到CST +0800 就是代表的中国标准时间;
- 时间戳:是从1970年1月1日(UTC/GMT的午夜 1970-01-01T00:00:00)开始所经过的秒数。
时间获取
Go中的time.Time代表一个纳秒精度的时间点,每一个时间点都具有一个地点信息,当计算时间的表示格式时,如Format、Hour和Year等方法,都会考虑该信息。
获取当前时间
time.Time 类型定义
// 注意:类型里包含三个未导出的字段
type Time struct {
wall uint64 // wall 和 ext 组成比较复杂
ext int64
loc *Location // 表示时区
}
获取时间、时间戳、周、天等
now := time.Now() // 注:time.Time 类型的时间
fmt.Printf("%v\n", now) // 输出:2021-09-16 14:54:56.253573 +0800 CST m=+0.432441917
timestamp := now.Unix() // 注:unix时间戳 秒 now.UnixNano() 纳秒时间戳
fmt.Printf("%v\n", timestamp) // 输出:1631775296
year := now.Year()
fmt.Printf("%v\n", year) // 输出:2021
month := now.Month() // 注:是`Month`类型数据
fmt.Printf("%v\n", month) // 输出:September String返回月份的英文名
// 还有 Day()、Hour()、Minute()、Second()等函数获取其他值
y, m, d := now.Date()
fmt.Printf("year:%d, month:%d, day:%d\n", y, m, d) // 输出:year:2021, month:9, day:16
// 返回星期
fmt.Println(now.Weekday()) // 输出:Thursday
//返回一年中对应的第几天
fmt.Println(now.YearDay()) // 输出:259
//返回时区
fmt.Println(now.Location()) // 输出:Local
格式化时间
需要注意的是格式化字符串不是常见的YmdHis,而是2006-01-02 15:04:05Go的成立日期。
now := time.Now()
fmt.Println(now.Format("2006-01-02 15:03:04")) // 输出:2021-09-16 15:03:28
fmt.Println(now.Format("2006.01.02")) // 输出:2021.09.16
fmt.Println(now.Format("15:03:04")) // 输出:15:03:31
时间转化
时间戳转time.Time
timestamp := time.Now().Unix() // 当前时间戳
t := time.Unix(timestamp,0) // 转化为 time.Time 格式
fmt.Println(t.Format("2006-01-02 15:04:05")) // 输出:2021-09-16 15:44:24
字符串转time.Time
time.Parse()方法使用的是UTC时间,和CST时间(中国标准时间)相差8小时,所以字符串转化为time.time时间时需要指定时区,可以使用time.ParseInLocation()方法。
t, _ := time.Parse("2006-01-02 15:04:05", "2021-01-10 15:01:02")
fmt.Println(t) // 输出:2021-09-16 15:49:02 +0000 UTC
str := time.Now().Format("2006-01-02 15:04:05") // 获取string格式当前时间
t, _ := time.ParseInLocation("2006-01-02 15:04:05", str, time.Local) // 指定本地时区
fmt.Println(t) // 输出:2021-09-16 15:54:59 +0800 CST
时间戳、time.Time、和字符串时间转化关系如下:
time.Unix() Time.Format()
int ------------> ------------> string
时间戳 Time.Unix() time.Time time.Parse() 日期字符串
<------------ <------------
time.ParseInLocation()
自定义结构体time.Time
在使用GORM的时候,发现查询数据的展示形式对开发不友好,那么我们可以自定义一个time.Tiem类型数据,并重写MarshalJSON方法。
type DateTime time.Time
// MarshalJSON 自定义解析json数据类型,将原始CST时间解析为标准时间格式
func (t DateTime) MarshalJSON() ([]byte, error) {
var stamp = fmt.Sprintf("\"%s\"", time.Time(t).Format("2006-01-02 15:04:05"))
return []byte(stamp), nil
}
// 定义基础Model 用我们自定义的 DateTime 类型定义时间字段
type Model struct {
ID uint `gorm:"column:id;autoIncrement;comment:'主键'" json:"id"`
CreatedAt DateTime `gorm:"column:created_at;default:null;comment:'创建时间'" json:"created_at"`
UpdatedAt DateTime `gorm:"column:updated_at;default:null;comment:'更新时间'" json:"updated_at"`
}
// gorm 查询数据
func (u *User) Find() {
users := []User{}
db.Limit(1).Find(&users)
// 格式化输出json
d, _ := json.MarshalIndent(users, "", " ")
fmt.Printf("查询结果:%v\n", string(d))
}
查询结果:
时间计算
时间区间引入一个新的类型Duration,Duration类型代表两个时间点之间经过的时间,以纳秒为单位。可表示的最长时间段大约290年。
type Duration int64
24小时内的时间增减
ParseDuration()方法解析一个时间段字符串。一个时间段字符串是一个序列,每个片段包含可选的正负号、十进制数、可选的小数部分和单位后缀如"300ms"、"-1.5h"、"2h45m"。合法的单位有"ns"、"us" 、"ms"、"s"、"m"、"h"。
now := time.Now()
fmt.Println(now) // 输出:2021-09-16 16:25:17.883157 +0800 CST m=+0.381067667
t, _ := time.ParseDuration("1h2m3s") // 1小时2分3s之后 注:有负号表示之前
fmt.Println(t) // 输出:1h2m3s
m := now.Add(t) // 当前时间点加t时间段
fmt.Println(m) // 输出:2021-09-16 17:26:48.754091 +0800 CST m=+3723.580913168
24小时外的时间增减
超过一天的时间增加减少的操作使用time.AddDate()方法,AddDate返回增加了给出的年份、月份和天数的时间点Time。例如,时间点January 1, 2011调用AddDate(-1, 2, 3)会返回March 4, 2010。
AddDate会将结果规范化,类似Date函数的做法。因此,举个例子,给时间点October 31添加一个月,会生成时间点December 1(从时间点November 31规范化而来)。
time.AddDate()方法的定义:
func (t Time) AddDate(years int, months int, days int) Time
其中参数years、months、days都是有符号的整型、负数代表减去对应数值后的时间点(之前的时间点),整数代表加上对应数值后的时间点(之后的时间点)
t, _ := time.ParseInLocation("2006-01-02 15:04:05", "2021-08-31 23:59:59", time.Local) // 将2021-08-31增加一个月和一天
fmt.Println(t) // 输出:2021-08-31 23:59:59 +0800 CST
m := t.AddDate(0, 1, 1)
fmt.Println(m) // 2021-10-02 23:59:59 +0800 CST
时期比较
有如下函数可以比较时间点的之前、之后、相等
func (t Time) Before(u Time) bool // 如果 t 代表的时间点在 u 之前,返回真;否则返回假。
func (t Time) After(u Time) bool // 如果 t 代表的时间点在 u 之后,返回真;否则返回假。
func (t Time) Equal(u Time) bool // 比较时间是否相等,相等返回真;否则返回假。
now := time.Now()
t, _ := time.ParseDuration("10s") // 1小时2分3s之后 注:有负号表示之前
m := now.Add(t) // 当前时间点加t时间段
if now.Before(m) {
fmt.Println(true) // 输出:true
}
时间区间
还有两个方法time.Since()、time.Until()计算给定时间和当前时间的时间差或当前时间和给定时间的时间差。
time.Since(t Time) Duration // 返回当前时间与 t 的时间差,返回值是 Duration
time.Until(t Time) Duration // 返回 t 与当前时间的时间差,返回值是 Duration
t, _ := time.ParseDuration("-1h")
m := now.Add(t)
fmt.Println(m)
// time.Since(m) 返回 Duration 类型时间段
// Hours 将时间段表示为float64类型的小时数
fmt.Println(time.Since(m).Hours()) // 输出:1.0000000214697222
fmt.Println(time.Until(m).Hours()) // 输出:-1.0000000325694445
计算函数执行时间可以这么实现:
// 计算函数执行时间
func main() {
start := time.Now()
defer func() {
fmt.Printf("%.2fs elapsed\n", time.Since(start).Seconds())
}()
// 后续逻辑
....
}