golang的時(shí)區(qū)和神奇的time.Parse的使用方法
時(shí)區(qū)
先寫一段測試代碼:
const TIME_LAYOUT = "2006-01-02 15:04:05" func parseWithLocation(name string, timeStr string) (time.Time, error) { locationName := name if l, err := time.LoadLocation(locationName); err != nil { println(err.Error()) return time.Time{}, err } else { lt, _ := time.ParseInLocation(TIME_LAYOUT, timeStr, l) fmt.Println(locationName, lt) return lt, nil } } func testTime() { fmt.Println("0. now: ", time.Now()) str := "2018-09-10 00:00:00" fmt.Println("1. str: ", str) t, _ := time.Parse(TIME_LAYOUT, str) fmt.Println("2. Parse time: ", t) tStr := t.Format(TIME_LAYOUT) fmt.Println("3. Format time str: ", tStr) name, offset := t.Zone() name2, offset2 := t.Local().Zone() fmt.Printf("4. Zone name: %v, Zone offset: %v\n", name, offset) fmt.Printf("5. Local Zone name: %v, Local Zone offset: %v\n", name2, offset2) tLocal := t.Local() tUTC := t.UTC() fmt.Printf("6. t: %v, Local: %v, UTC: %v\n", t, tLocal, tUTC) fmt.Printf("7. t: %v, Local: %v, UTC: %v\n", t.Format(TIME_LAYOUT), tLocal.Format(TIME_LAYOUT), tUTC.Format(TIME_LAYOUT)) fmt.Printf("8. Local.Unix: %v, UTC.Unix: %v\n", tLocal.Unix(), tUTC.Unix()) str2 := "1969-12-31 23:59:59" t2, _ := time.Parse(TIME_LAYOUT, str2) fmt.Printf("9. str2:%v,time: %v, Unix: %v\n", str2, t2, t2.Unix()) fmt.Printf("10. %v, %v\n", tLocal.Format(time.ANSIC), tUTC.Format(time.ANSIC)) fmt.Printf("11. %v, %v\n", tLocal.Format(time.RFC822), tUTC.Format(time.RFC822)) fmt.Printf("12. %v, %v\n", tLocal.Format(time.RFC822Z), tUTC.Format(time.RFC822Z)) //指定時(shí)區(qū) parseWithLocation("America/Cordoba", str) parseWithLocation("Asia/Shanghai", str) parseWithLocation("Asia/Beijing", str) } testTime()
輸出:
0. now: 2018-09-19 19:06:07.3642781 +0800 CST m=+0.005995601
1. str: 2018-09-10 00:00:00
2. Parse time: 2018-09-10 00:00:00 +0000 UTC
3. Format time str: 2018-09-10 00:00:00
4. Zone name: UTC, Zone offset: 0
5. Local Zone name: CST, Local Zone offset: 28800
6. t: 2018-09-10 00:00:00 +0000 UTC, Local: 2018-09-10 08:00:00 +0800 CST, UTC: 2018-09-10 00:00:00 +0000 UTC
7. t: 2018-09-10 00:00:00, Local: 2018-09-10 08:00:00, UTC: 2018-09-10 00:00:00
8. Local.Unix: 1536537600, UTC.Unix: 1536537600
9. str2:1969-12-31 23:59:59,time: 1969-12-31 23:59:59 +0000 UTC, Unix: -1
10. Mon Sep 10 08:00:00 2018, Mon Sep 10 00:00:00 2018
11. 10 Sep 18 08:00 CST, 10 Sep 18 00:00 UTC
12. 10 Sep 18 08:00 +0800, 10 Sep 18 00:00 +0000
America/Cordoba 2018-09-10 00:00:00 -0300 -03
Asia/Shanghai 2018-09-10 00:00:00 +0800 CST
cannot find Asia/Beijing in zip file C:\Go\/lib/time/zoneinfo.zip
從以上代碼的測試結(jié)果可以得出幾點(diǎn):
- time.Now 得到的當(dāng)前時(shí)間的時(shí)區(qū)跟電腦的當(dāng)前時(shí)區(qū)一樣。
- time.Parse 把時(shí)間字符串轉(zhuǎn)換為Time,時(shí)區(qū)是UTC時(shí)區(qū)。
- 不管Time變量存儲的是什么時(shí)區(qū),其Unix()方法返回的都是距離UTC時(shí)間:1970年1月1日0點(diǎn)0分0秒的秒數(shù)。
- Unix()返回的秒數(shù)可以是負(fù)數(shù),如果時(shí)間小于1970-01-01 00:00:00的話。
- Zone方法可以獲得變量的時(shí)區(qū)和時(shí)區(qū)與UTC的偏移秒數(shù),應(yīng)該支持夏令時(shí)和冬令時(shí)。
- time.LoadLocation可以根據(jù)時(shí)區(qū)名創(chuàng)建時(shí)區(qū)Location,所有的時(shí)區(qū)名字可以在$GOROOT/lib/time/zoneinfo.zip文件中找到,解壓zoneinfo.zip可以得到一堆目錄和文件,我們只需要目錄和文件的名字,時(shí)區(qū)名是目錄名+文件名,比如"Asia/Shanghai"。中國時(shí)區(qū)名只有"Asia/Shanghai"和"Asia/Chongqing",而沒有"Asia/Beijing"。
- time.ParseInLocation可以根據(jù)時(shí)間字符串和指定時(shí)區(qū)轉(zhuǎn)換Time。
- 感謝中國只有一個(gè)時(shí)區(qū)而且沒有夏令時(shí)和冬令時(shí),可怕的美國居然有6個(gè)時(shí)區(qū),想想都可怕。
神奇的time.Parse
一開始使用time.Parse時(shí)很不習(xí)慣,因?yàn)榉浅F婀值膌ayout參數(shù)。
除了golang自帶定義的layout:
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" )
還可以自定義layout,比如:
"2006-01-02 15:04:05"
網(wǎng)上基本上都在傳說這個(gè)日子是golang項(xiàng)目開始創(chuàng)建的時(shí)間,為了紀(jì)念生日才這樣設(shè)計(jì),其實(shí)這真是無稽之談瞎扯淡。
網(wǎng)上文章沒有找到說的比較清楚的,幸好有源碼,打開time.Parse的源碼看了一下,發(fā)現(xiàn)這個(gè)設(shè)計(jì)很好很科學(xué)。
解析layout的主要代碼在nextStdChunk方法中:
// nextStdChunk finds the first occurrence of a std string in // layout and returns the text before, the std string, and the text after. func nextStdChunk(layout string) (prefix string, std int, suffix string) { for i := 0; i < len(layout); i++ { switch c := int(layout[i]); c { case 'J': // January, Jan if len(layout) >= i+3 && layout[i:i+3] == "Jan" { if len(layout) >= i+7 && layout[i:i+7] == "January" { return layout[0:i], stdLongMonth, layout[i+7:] } if !startsWithLowerCase(layout[i+3:]) { return layout[0:i], stdMonth, layout[i+3:] } } case 'M': // Monday, Mon, MST if len(layout) >= i+3 { if layout[i:i+3] == "Mon" { if len(layout) >= i+6 && layout[i:i+6] == "Monday" { return layout[0:i], stdLongWeekDay, layout[i+6:] } if !startsWithLowerCase(layout[i+3:]) { return layout[0:i], stdWeekDay, layout[i+3:] } } if layout[i:i+3] == "MST" { return layout[0:i], stdTZ, layout[i+3:] } } case '0': // 01, 02, 03, 04, 05, 06 if len(layout) >= i+2 && '1' <= layout[i+1] && layout[i+1] <= '6' { return layout[0:i], std0x[layout[i+1]-'1'], layout[i+2:] } case '1': // 15, 1 if len(layout) >= i+2 && layout[i+1] == '5' { return layout[0:i], stdHour, layout[i+2:] } return layout[0:i], stdNumMonth, layout[i+1:] case '2': // 2006, 2 if len(layout) >= i+4 && layout[i:i+4] == "2006" { return layout[0:i], stdLongYear, layout[i+4:] } return layout[0:i], stdDay, layout[i+1:] case '_': // _2, _2006 if len(layout) >= i+2 && layout[i+1] == '2' { //_2006 is really a literal _, followed by stdLongYear if len(layout) >= i+5 && layout[i+1:i+5] == "2006" { return layout[0 : i+1], stdLongYear, layout[i+5:] } return layout[0:i], stdUnderDay, layout[i+2:] } case '3': return layout[0:i], stdHour12, layout[i+1:] case '4': return layout[0:i], stdMinute, layout[i+1:] case '5': return layout[0:i], stdSecond, layout[i+1:] case 'P': // PM if len(layout) >= i+2 && layout[i+1] == 'M' { return layout[0:i], stdPM, layout[i+2:] } case 'p': // pm if len(layout) >= i+2 && layout[i+1] == 'm' { return layout[0:i], stdpm, layout[i+2:] } case '-': // -070000, -07:00:00, -0700, -07:00, -07 if len(layout) >= i+7 && layout[i:i+7] == "-070000" { return layout[0:i], stdNumSecondsTz, layout[i+7:] } if len(layout) >= i+9 && layout[i:i+9] == "-07:00:00" { return layout[0:i], stdNumColonSecondsTZ, layout[i+9:] } if len(layout) >= i+5 && layout[i:i+5] == "-0700" { return layout[0:i], stdNumTZ, layout[i+5:] } if len(layout) >= i+6 && layout[i:i+6] == "-07:00" { return layout[0:i], stdNumColonTZ, layout[i+6:] } if len(layout) >= i+3 && layout[i:i+3] == "-07" { return layout[0:i], stdNumShortTZ, layout[i+3:] } case 'Z': // Z070000, Z07:00:00, Z0700, Z07:00, if len(layout) >= i+7 && layout[i:i+7] == "Z070000" { return layout[0:i], stdISO8601SecondsTZ, layout[i+7:] } if len(layout) >= i+9 && layout[i:i+9] == "Z07:00:00" { return layout[0:i], stdISO8601ColonSecondsTZ, layout[i+9:] } if len(layout) >= i+5 && layout[i:i+5] == "Z0700" { return layout[0:i], stdISO8601TZ, layout[i+5:] } if len(layout) >= i+6 && layout[i:i+6] == "Z07:00" { return layout[0:i], stdISO8601ColonTZ, layout[i+6:] } if len(layout) >= i+3 && layout[i:i+3] == "Z07" { return layout[0:i], stdISO8601ShortTZ, layout[i+3:] } case '.': // .000 or .999 - repeated digits for fractional seconds. if i+1 < len(layout) && (layout[i+1] == '0' || layout[i+1] == '9') { ch := layout[i+1] j := i + 1 for j < len(layout) && layout[j] == ch { j++ } // String of digits must end here - only fractional second is all digits. if !isDigit(layout, j) { std := stdFracSecond0 if layout[i+1] == '9' { std = stdFracSecond9 } std |= (j - (i + 1)) << stdArgShift return layout[0:i], std, layout[j:] } } } } return layout, 0, "" }
可以發(fā)現(xiàn)layout的所有代表年月日時(shí)分秒甚至?xí)r區(qū)的值都是互斥不相等的。
比如年份:短年份06,長年份2006,
月份:01,Jan,January
日:02,2,_2
時(shí):15,3,03
分:04, 4
秒:05, 5
因?yàn)槎疾幌嗟人酝ㄟ^遍歷layout就可以switch case解析出每個(gè)區(qū)塊的意義和在字符串中的位置,這樣輸入對應(yīng)格式的時(shí)間字符串就可以順利解析出來。
這樣layout也可以自定義,而且順序任意,只要符合下列每個(gè)區(qū)塊定義的規(guī)則即可,
代碼中的注釋就是規(guī)則寫法:
const ( _ = iota stdLongMonth = iota + stdNeedDate // "January" stdMonth // "Jan" stdNumMonth // "1" stdZeroMonth // "01" stdLongWeekDay // "Monday" stdWeekDay // "Mon" stdDay // "2" stdUnderDay // "_2" stdZeroDay // "02" stdHour = iota + stdNeedClock // "15" stdHour12 // "3" stdZeroHour12 // "03" stdMinute // "4" stdZeroMinute // "04" stdSecond // "5" stdZeroSecond // "05" stdLongYear = iota + stdNeedDate // "2006" stdYear // "06" stdPM = iota + stdNeedClock // "PM" stdpm // "pm" stdTZ = iota // "MST" stdISO8601TZ // "Z0700" // prints Z for UTC stdISO8601SecondsTZ // "Z070000" stdISO8601ShortTZ // "Z07" stdISO8601ColonTZ // "Z07:00" // prints Z for UTC stdISO8601ColonSecondsTZ // "Z07:00:00" stdNumTZ // "-0700" // always numeric stdNumSecondsTz // "-070000" stdNumShortTZ // "-07" // always numeric stdNumColonTZ // "-07:00" // always numeric stdNumColonSecondsTZ // "-07:00:00" stdFracSecond0 // ".0", ".00", ... , trailing zeros included stdFracSecond9 // ".9", ".99", ..., trailing zeros omitted stdNeedDate = 1 << 8 // need month, day, year stdNeedClock = 2 << 8 // need hour, minute, second stdArgShift = 16 // extra argument in high bits, above low stdArgShift stdMask = 1<<stdArgShift - 1 // mask out argument )
時(shí)區(qū):
時(shí)區(qū)使用:MST
時(shí)區(qū)偏移使用-0700或者Z0700等等。
下面是一個(gè)使用時(shí)區(qū)的例子,Z0700比較特殊,當(dāng)輸入時(shí)間直接使用Z時(shí)就直接代表UTC時(shí)區(qū)。
func testTimeParse() { t, _ := time.Parse("2006-01-02 15:04:05 -0700 MST", "2018-09-20 15:39:06 +0800 CST") fmt.Println(t) t, _ = time.Parse("2006-01-02 15:04:05 -0700 MST", "2018-09-20 15:39:06 +0000 CST") fmt.Println(t) t, _ = time.Parse("2006-01-02 15:04:05 Z0700 MST", "2018-09-20 15:39:06 +0800 CST") fmt.Println(t) t, _ = time.Parse("2006-01-02 15:04:05 Z0700 MST", "2018-09-20 15:39:06 Z GMT") fmt.Println(t) t, _ = time.Parse("2006-01-02 15:04:05 Z0700 MST", "2018-09-20 15:39:06 +0000 GMT") fmt.Println(t) }
輸出:
2018-09-20 15:39:06 +0800 CST
2018-09-20 15:39:06 +0000 CST
2018-09-20 15:39:06 +0800 CST
2018-09-20 15:39:06 +0000 UTC
2018-09-20 15:39:06 +0000 GMT
還有疑問的可以看看go自帶的測試?yán)樱篏o/src/time/example_test.go
到此這篇關(guān)于golang的時(shí)區(qū)和神奇的time.Parse的使用方法的文章就介紹到這了,更多相關(guān)golang的時(shí)區(qū)和time.Parse內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- golang的time包:秒、毫秒、納秒時(shí)間戳輸出方式
- 解決golang時(shí)間字符串轉(zhuǎn)time.Time的坑
- Golang 定時(shí)器(Timer 和 Ticker),這篇文章就夠了
- golang time包下定時(shí)器的實(shí)現(xiàn)方法
- golang中time包之時(shí)間間隔格式化和秒、毫秒、納秒等時(shí)間戳格式輸出的方法實(shí)例
- golang 使用time包獲取時(shí)間戳與日期格式化操作
- golang time包的用法詳解
- golang time包做時(shí)間轉(zhuǎn)換操作
- golang1.23版本之前 Timer Reset方法無法正確使用
相關(guān)文章
golang中for循環(huán)遍歷channel時(shí)需要注意的問題詳解
這篇文章主要給大家介紹了關(guān)于golang中for循環(huán)遍歷channel時(shí)需要注意的問題的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2018-04-04go?install和go?get的區(qū)別實(shí)例詳解
go install是Golang用來編譯和安裝自定義package的工具,下面這篇文章主要給大家介紹了關(guān)于go?install和go?get區(qū)別的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-01-01golang實(shí)現(xiàn)一個(gè)簡單的websocket聊天室功能
這篇文章主要介紹了golang實(shí)現(xiàn)一個(gè)簡單的websocket聊天室功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-10-10golang Gorm與數(shù)據(jù)庫完整性約束詳解
這篇文章主要介紹了golang Gorm與數(shù)據(jù)庫完整性約束詳解,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-12-12