摘要:
时间包提供显示和测量时间的功能。日历使用公历计算。

time包提供了时间的显示和测量用的函数。日历的计算采用的是公历。 time 类型

type Time struct {
// wall and ext encode the wall time seconds, wall time nanoseconds,
// and optional monotonic clock reading in nanoseconds.
//
// From high to low bit position, wall encodes a 1-bit flag (hasMonotonic),
// a 33-bit seconds field, and a 30-bit wall time nanoseconds field.
// The nanoseconds field is in the range [0, 999999999].
// If the hasMonotonic bit is 0, then the 33-bit field must be zero
// and the full signed 64-bit wall seconds since Jan 1 year 1 is stored in ext.
// If the hasMonotonic bit is 1, then the 33-bit field holds a 33-bit
// unsigned wall seconds since Jan 1 year 1885, and ext holds a
// signed 64-bit monotonic clock reading, nanoseconds since process start.
wall uint64
ext  int64

	// loc specifies the Location that should be used to
	// determine the minute, hour, month, day, and year
	// that correspond to this Time.
	// The nil location means UTC.
	// All UTC times are represented with loc==nil, never loc==&utcLoc.
	loc *Location
}

time可以精确到纳秒, 下面是各种时间换

package main

import (
	"fmt"
	"time"
)

func main() {
	t := time.Now()
	fmt.Println(t)        //获取当前时间2021-09-23 10:55:44.831571 +0800 CST m=+0.000090412
	fmt.Println(t.Unix()) //获取当前时间时间戳 1632366278
	//fmt.Println(t.UnixMilli()) //获取当前时间毫秒 1632366278605
	//fmt.Println(t.UnixMicro()) //获取当前时间微秒 1632366278605122
	fmt.Println(t.UnixNano()) //获取当前时间时纳秒 1632366278605122000
	fmt.Println(t.Hour())     //获取当前小时 10
	fmt.Println(t.Day())      //获取当前天 23
	fmt.Println(t.Weekday())  //获取当前周Thursday
	fmt.Println(t.ISOWeek())  //获取当前周2021 38

	//格式化当前时间表示
	fmt.Println(t.String())                      //字符型
	fmt.Println(t.Format("2006-01-02 15:04:05")) //2021-09-23 11:12:42
	fmt.Println(t.Format("2006-01-02"))          //2021-09-23
	fmt.Println(t.Format("15:04:05"))            //11:12:42

	//指定时间戳转换
	fmt.Println(time.Unix(1632366278, 0).Format("2006-01-02 15:04:05")) //2021-09-23 11:04:38
	//指定时间转时间戳
	tm2, _ := time.Parse("2006-01-02 15:04:05", "2021-09-23 11:04:38")
	fmt.Println(tm2.Unix()) //1632395078

	//"2021-09-08T08:18:46+08:00" 转2021-09-08 08:18:46
	t, _ = time.Parse(time.RFC3339, "2021-09-08T08:18:46+08:00")
	fmt.Println(t.Format("2006-01-02 15:04:05"))
	//2021-09-07T17:01:34.182659088Z 转2021-09-07 17:01:34
	t, _ = time.Parse(time.RFC3339Nano, "2021-09-07T17:01:34.182659088Z")
	fmt.Println(t.Format("2006-01-02 15:04:05"))
	//其他格式类似可参考

	//ANSIC       = "Mon Jan _2 15:04:05 2006"
	//UnixDate    = "Mon Jan _2 15:04:05 MST 2006"
	//RubyDate    = "Mon Jan 02 15:04:05 -0700 2006"
	//RFC822      = "02 Jan 06 15:04 MST"
	//RFC822Z     = "02 Jan 06 15:04 -0700" // 使用数字表示时区的RFC822
	//RFC850      = "Monday, 02-Jan-06 15:04:05 MST"
	//RFC1123     = "Mon, 02 Jan 2006 15:04:05 MST"
	//RFC1123Z    = "Mon, 02 Jan 2006 15:04:05 -0700" // 使用数字表示时区的RFC1123
	//RFC3339     = "2006-01-02T15:04:05Z07:00"
	//RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
	//Kitchen     = "3:04PM"
	//// 方便的时间戳
	//Stamp      = "Jan _2 15:04:05"
	//StampMilli = "Jan _2 15:04:05.000"
	//StampMicro = "Jan _2 15:04:05.000000"
	//StampNano  = "Jan _2 15:04:05.000000000"

	//设置时区 Location
	//默认UTC
	loc, _ := time.LoadLocation("")
	// 服务器设定的时区,一般为CST
	//loc, _ := time.LoadLocation("Local")
	//loc, _ := time.LoadLocation("Asia/Shanghai")
	t.In(loc).Format("2006-01-02 15:04:05")

	//1.5s后
	now := time.Now()
	tp, _ := time.ParseDuration("1.5s")
	fmt.Println(tp, tp.Truncate(1000), tp.Seconds(), tp.Nanoseconds())
	m1 := now.Add(tp)
	fmt.Println(m1) //2021-09-23 14:30:42.006213 +0800 CST m=+1.500352171

	//1个小时前
	tp, _ = time.ParseDuration("-1h")

	m1 = now.Add(tp)
	fmt.Println(m1) //2021-09-23 13:30:40.506213 +0800 CST m=-3599.999647829
	//休眠时间
	//time.Sleep(time.Duration(10) * time.Second)

	// func After(d Duration) <-chan Time  非阻塞,可用于延迟
	//time.After(time.Duration(10) * time.Second)

	// func Since(t Time) Duration 两个时间点的间隔
	start := time.Now()
	fmt.Println(time.Since(start)) // 等价于 Now().Sub(t), 可用来计算一段业务的消耗时间

	//func Until(t Time) Duration     //  等价于 t.Sub(Now()),t与当前时间的间隔

	time3 := "2021-03-20 08:50:29"
	time4 := "2021-03-20 08:50:29"
	//先把时间字符串格式化成相同的时间类型
	t3, _ := time.Parse("2006-01-02 15:04:05", time3)
	t4, _ := time.Parse("2006-01-02 15:04:05", time4)

	fmt.Println(t3.Equal(t4)) //true

	now = time.Now()
	//Ticker 类型包含一个 channel,有时我们会遇到每隔一段时间执行的业务(比如设置心跳时间等),就可以用它来处理,这是一个重复的过程

	// 无法取消
	//tick := time.Tick(1 * time.Minute)
	//for _ = range tick {
	//	// do something
	//}
	//NewTicker返回一个新的Ticker,该Ticker包含一个通道字段,并会每隔时间段d就向该通道发送当时的时间。它会调整时间间隔或者丢弃tick信息以适应反应慢的接收者。如果d<=0会panic。关闭该Ticker可以释放相关资源。
	// 可通过调用ticker.Stop取消
	//创建一个周期性的定时器
	//设置定时器为3秒
	timer := time.NewTimer(3 * time.Second)
	fmt.Println("当前时间为:", time.Now())

	t = <-timer.C //从定时器拿数据
	fmt.Println("当前时间为:", t)
	timer.Stop() //停止

}

问题:在使用 gorm 的过程中, 处理时间戳字段时遇到问题。写时间戳到数据库时无法写入。

设置数据库的 dsn

parseTime = "True"
loc = "Local"

设置自定义 Time 类型

package types

import (
	"database/sql/driver"
	"fmt"
	"time"
)

// Time is alias type for time.Time
type Time time.Time

const (
	timeFormart = "2006-01-02 15:04:05"
	zone        = "Asia/Shanghai"
)

// UnmarshalJSON implements json unmarshal interface.
func (t *Time) UnmarshalJSON(data []byte) (err error) {
	now, err := time.ParseInLocation(`"`+timeFormart+`"`, string(data), time.Local)
	*t = Time(now)
	return
}

// MarshalJSON implements json marshal interface.
func (t Time) MarshalJSON() ([]byte, error) {
	b := make([]byte, 0, len(timeFormart)+2)
	b = append(b, '"')
	b = time.Time(t).AppendFormat(b, timeFormart)
	b = append(b, '"')
	return b, nil
}

func (t Time) String() string {
	return time.Time(t).Format(timeFormart)
}

func (t Time) local() time.Time {
	loc, _ := time.LoadLocation(zone)
	return time.Time(t).In(loc)
}

// Value ...
func (t Time) Value() (driver.Value, error) {
	var zeroTime time.Time
	var ti = time.Time(t)
	if ti.UnixNano() == zeroTime.UnixNano() {
		return nil, nil
	}
	return ti, nil
}

// Scan valueof time.Time 注意是指针类型 method
func (t *Time) Scan(v interface{}) error {
	value, ok := v.(time.Time)
	if ok {
		*t = Time(value)
		return nil
	}
	return fmt.Errorf("can not convert %v to timestamp", v)
}

这样程序中所有的时间值都使用types.Time类型就可以准确进行时间戳变量的读写操作。

原理:其实就是自定义数据库数据类型,在 sql driver 中实现自定义类型需要实现 Scanner和Valuer接口

//Scanner
type Scanner interface {
	Scan(src interface{}) error
}

//Valuer

type Valuer interface {
	// Value returns a driver Value.
	Value() (Value, error)
}

//unmarshal和marshal 自定义 json 转换格式