时间格式化
最近在处理gps数据时候发现时间时区转换的问题,以及以前碰到的go连接数据库时间时区的问题,还有对数据进行统计时对时间的计算
字符串->格式化时间
layout格式化标准 在time包的format.go中69-86行
const (
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 with numeric zone
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 with numeric zone
RFC3339 = "2006-01-02T15:04:05Z07:00"
RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
Kitchen = "3:04PM"
// Handy time stamps.
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"
)
http协议中的date格式 layout 在go1.12 net/http/server.go 911行
const TimeFormat = "Mon, 02 Jan 2006 15:04:05 GMT"
以上layout在format平时可能不常用,但是在解析的时候却常用,需要自行定义:"2006-01-02 15:04:05"
->先将字符串解析成Time类型,然后再将其.format("自己想要的时间格式")
//unix时间格式->本地utc时间
parse, e := time.Parse(time.UnixDate, "Mon Jan 2 15:04:05 MST 2006")
fmt.Println(parse.format("15:04:05"), e)
//指定时区时间cst时间
location, e := time.ParseInLocation("06/01/02 15:04:05", "19/03/29 15:26:04", time.Local)
fmt.Println(location, e)
---------------------------------------
时间计算(主要涉及时区的)
MySQL驱动解析时间的前提是连接字符串加了parseTime和loc, 如果parseTime为false,
会把mysql的date类型变成[]byte/string自行处理, parseTime为true才处理时间,
loc指定MySQL中存储时间数据的时区, 如果没有指定loc, 用UTC. 序列化和反序列化均使用连接字符串中的设定的loc,
SQL语句中的time.Time类型的参数的时区信息如果和loc不同,
则会调用t.In(loc)方法转时区.
!!!注意只要MySQL连接字符串设置了parseTime=true, 就会解析时间, 不管你是用string还是time.Time接收的.
所以在项目中如果涉及多种语言开发公用同一种数据库则要特别注意时间时区转换问题
在golang时间深入理解这篇博客中就有他深入的坑,个人就觉得存字符串是一个不错的选择,然后在程序中对时区手动转换
---------------------------------
对时间进行计算我们需要先获得一个Time类型的变量
//time.Now() ->获取当前时间 2019-03-29 17:43:50.8109076 +0800 CST m=+0.014994001
//time.ParseInLocation($layout,$value,time.Local) ->字符串解析后的指定时区的时间类型
//time.Parse($layout,$value) ->字符串解析后为utc时间
---------------------------------
此时time包提供了以下函数便于对时间的计算:
Sub/Add函数便于处理到分钟秒时间的加减
Add(Dutation类型):加(正时间或者负时间)
eg:
参数:
// ParseDuration parses a duration string.
// A duration string is a possibly signed sequence of
// decimal numbers, each with optional fraction and a unit suffix,
// such as "300ms", "-1.5h" or "2h45m".
// Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h
duration, _ := time.ParseDuration("10s")
fmt.Println(time.Now())//2019-03-29 18:30:11.9405942 +0800 CST m=+0.013993301
fmt.Println(time.Now().Add(duration))//2019-03-29 18:30:22.0245444 +0800 CST m=+10.097943601
---------------------------------
// Sub returns the duration t-u. If the result exceeds the maximum (or minimum)
// value that can be stored in a Duration, the maximum (or minimum) duration
// will be returned.
// To compute t-d for a duration d, use t.Add(-d).
Sub(Time类型):当前时间与指定时间的差值[也可以使用Add方法传入一个负时间]
eg:
duration, _ := time.ParseDuration("10s")
fmt.Println(time.Now().Sub(time.Now().Add(duration)))//-10s
---------------------------------
AddDate(年[int],月[int],日[int]) :用于处理对年月日时间的加减
eg:
fmt.Println(time.Now())//2019-03-29 18:28:19.2330785 +0800 CST m=+0.015990501
fmt.Println(time.Now().AddDate(0,0,-10))//2019-03-19 18:28:19.3000548 +0800 CST
---------------------------------
Before(Time类型):判断当前时间是否在传入时间之前
eg:
duration, _ := time.ParseDuration("10s")
fmt.Println(time.Now().Add(duration).Before(time.Now())) //false
After(Time类型):判断当前时间是否在传入时间之后
eg:
duration, _ := time.ParseDuration("10s")
fmt.Println(time.Now().Add(duration).After(time.Now())) //true
---------------------------------
// Since returns the time elapsed since t.
// It is shorthand for time.Now().Sub(t).
time.Since(Time类型):time.Now().Sub(t)的缩减版
eg:
fmt.Println(time.Since(time.Now().Local())) //0s
---------------------------------
fmt.Println(time.Now().Month()):将会得到英文的月份,此时可以通过int强转为数字
定时器
// NewTicker returns a new Ticker containing a channel that will send the
// time with a period specified by the duration argument.
// It adjusts the intervals or drops ticks to make up for slow receivers.
// The duration d must be greater than zero; if not, NewTicker will panic.
// Stop the ticker to release associated resources.
newTicker := time.NewTicker(time.Second)
for {
<-newTicker.C
fmt.Println(time.Now())
}
// Stop turns off a ticker. After Stop, no more ticks will be sent.
// Stop does not close the channel, to prevent a concurrent goroutine
// reading from the channel from seeing an erroneous "tick".
newTicker.Stop() : 没有关闭.C这个channel只是阻止了数据的写入
一定要记得stop释放资源,ticker.C常常放在goroutine中的select,这时通常用的是给一个chan让groutine安全退出
eg:
import (
"fmt"
"time"
)
func main() {
transData := make(chan int, 1)
go Test(transData)
for v := range transData {
fmt.Println(v)
}
}
func Test(transData chan int) {
timeout := time.After(10 * time.Second)
ticker := time.NewTicker(1 * time.Second)
go func() {
i := 0
for {
<-ticker.C
transData <- i
}
}()
<-timeout
ticker.Stop()
}
当执行到ticker stop之后并没有close掉,导致goroutine资源泄漏 而.C的channl阻止了数据的写入导致了deadblock的错误,这种情况可以通过一个notify通知关闭