時間格式化
最近在處理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通知關閉