本文共 13831 字,大约阅读时间需要 46 分钟。
时区
先写一段测试代码:
1const TIME_LAYOUT = "2006-01-02 15:04:05" 2 3func parseWithLocation(name string, timeStr string) (time.Time, error) { 4 locationName := name 5 if l, err := time.LoadLocation(locationName); err != nil { 6 println(err.Error()) 7 return time.Time{}, err 8 } else { 9 lt, _ := time.ParseInLocation(TIME_LAYOUT, timeStr, l) 10 fmt.Println(locationName, lt) 11 return lt, nil 12 } 13} 14func testTime() { 15 fmt.Println("0. now: ", time.Now()) 16 str := "2018-09-10 00:00:00" 17 fmt.Println("1. str: ", str) 18 t, _ := time.Parse(TIME_LAYOUT, str) 19 fmt.Println("2. Parse time: ", t) 20 tStr := t.Format(TIME_LAYOUT) 21 fmt.Println("3. Format time str: ", tStr) 22 name, offset := t.Zone() 23 name2, offset2 := t.Local().Zone() 24 fmt.Printf("4. Zone name: %v, Zone offset: %v\n", name, offset) 25 fmt.Printf("5. Local Zone name: %v, Local Zone offset: %v\n", name2, offset2) 26 tLocal := t.Local() 27 tUTC := t.UTC() 28 fmt.Printf("6. t: %v, Local: %v, UTC: %v\n", t, tLocal, tUTC) 29 fmt.Printf("7. t: %v, Local: %v, UTC: %v\n", t.Format(TIME_LAYOUT), tLocal.Format(TIME_LAYOUT), tUTC.Format(TIME_LAYOUT)) 30 fmt.Printf("8. Local.Unix: %v, UTC.Unix: %v\n", tLocal.Unix(), tUTC.Unix()) 31 str2 := "1969-12-31 23:59:59" 32 t2, _ := time.Parse(TIME_LAYOUT, str2) 33 fmt.Printf("9. str2:%v,time: %v, Unix: %v\n", str2, t2, t2.Unix()) 34 fmt.Printf("10. %v, %v\n", tLocal.Format(time.ANSIC), tUTC.Format(time.ANSIC)) 35 fmt.Printf("11. %v, %v\n", tLocal.Format(time.RFC822), tUTC.Format(time.RFC822)) 36 fmt.Printf("12. %v, %v\n", tLocal.Format(time.RFC822Z), tUTC.Format(time.RFC822Z)) 37 38 //指定时区 39 parseWithLocation("America/Cordoba", str) 40 parseWithLocation("Asia/Shanghai", str) 41 parseWithLocation("Asia/Beijing", str) 42} 43testTime()
输出:
10. now: 2018-09-19 19:06:07.3642781 +0800 CST m=+0.005995601 21. str: 2018-09-10 00:00:00 32. Parse time: 2018-09-10 00:00:00 +0000 UTC 43. Format time str: 2018-09-10 00:00:00 54. Zone name: UTC, Zone offset: 0 65. Local Zone name: CST, Local Zone offset: 28800 76. 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 87. t: 2018-09-10 00:00:00, Local: 2018-09-10 08:00:00, UTC: 2018-09-10 00:00:00 98. Local.Unix: 1536537600, UTC.Unix: 1536537600 109. str2:1969-12-31 23:59:59,time: 1969-12-31 23:59:59 +0000 UTC, Unix: -1 1110. Mon Sep 10 08:00:00 2018, Mon Sep 10 00:00:00 2018 1211. 10 Sep 18 08:00 CST, 10 Sep 18 00:00 UTC 1312. 10 Sep 18 08:00 +0800, 10 Sep 18 00:00 +0000 14America/Cordoba 2018-09-10 00:00:00 -0300 -03 15Asia/Shanghai 2018-09-10 00:00:00 +0800 CST 16cannot find Asia/Beijing in zip file C:\Go\/lib/time/zoneinfo.zip
从以上代码的测试结果可以得出几点:
● time.Now
得到的当前时间的时区跟电脑的当前时区一样。 ● time.Parse
把时间字符串转换为Time,时区是UTC时区。 ● 不管Time变量存储的是什么时区,其Unix()
方法返回的都是距离UTC时间:1970年1月1日0点0分0秒的秒数。 ● Unix()
返回的秒数可以是负数,如果时间小于1970-01-01 00:00:00的话。 ● Zone
方法可以获得变量的时区和时区与UTC的偏移秒数,应该支持夏令时和冬令时。 ● time.LoadLocation
可以根据时区名创建时区Location
,所有的时区名字可以在$GOROOT/lib/time/zoneinfo.zip
文件中找到,解压zoneinfo.zip
可以得到一堆目录和文件,我们只需要目录和文件的名字,时区名是目录名+文件名,比如"Asia/Shanghai"。中国时区名只有"Asia/Shanghai"和"Asia/Chongqing",而没有"Asia/Beijing"。 ● time.ParseInLocation
可以根据时间字符串和指定时区转换Time。
● 感谢中国只有一个时区而且没有夏令时和冬令时,可怕的美国居然有6个时区,想想都可怕。
神奇的time.Parse
一开始使用time.Parse
时很不习惯,因为非常奇怪的layout
参数。
layout
: 1const ( 2 ANSIC = "Mon Jan _2 15:04:05 2006" 3 UnixDate = "Mon Jan _2 15:04:05 MST 2006" 4 RubyDate = "Mon Jan 02 15:04:05 -0700 2006" 5 RFC822 = "02 Jan 06 15:04 MST" 6 RFC822Z = "02 Jan 06 15:04 -0700" // RFC822 with numeric zone 7 RFC850 = "Monday, 02-Jan-06 15:04:05 MST" 8 RFC1123 = "Mon, 02 Jan 2006 15:04:05 MST" 9 RFC1123Z = "Mon, 02 Jan 2006 15:04:05 -0700" // RFC1123 with numeric zone 10 RFC3339 = "2006-01-02T15:04:05Z07:00" 11 RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00" 12 Kitchen = "3:04PM" 13 // Handy time stamps. 14 Stamp = "Jan _2 15:04:05" 15 StampMilli = "Jan _2 15:04:05.000" 16 StampMicro = "Jan _2 15:04:05.000000" 17 StampNano = "Jan _2 15:04:05.000000000" 18)
还可以自定义layout,比如:
1"2006-01-02 15:04:05"
网上基本上都在传说这个日子是golang项目开始创建的时间,为了纪念生日才这样设计,其实这真是无稽之谈瞎扯淡。
网上文章没有找到说的比较清楚的,幸好有源码,打开time.Parse
的源码看了一下,发现这个设计很好很科学。 解析layout的主要代码在nextStdChunk
方法中: 1// nextStdChunk finds the first occurrence of a std string in 2// layout and returns the text before, the std string, and the text after. 3func nextStdChunk(layout string) (prefix string, std int, suffix string) { 4 for i := 0; i < len(layout); i++ { 5 switch c := int(layout[i]); c { 6 case 'J': // January, Jan 7 if len(layout) >= i+3 && layout[i:i+3] == "Jan" { 8 if len(layout) >= i+7 && layout[i:i+7] == "January" { 9 return layout[0:i], stdLongMonth, layout[i+7:] 10 } 11 if !startsWithLowerCase(layout[i+3:]) { 12 return layout[0:i], stdMonth, layout[i+3:] 13 } 14 } 15 16 case 'M': // Monday, Mon, MST 17 if len(layout) >= i+3 { 18 if layout[i:i+3] == "Mon" { 19 if len(layout) >= i+6 && layout[i:i+6] == "Monday" { 20 return layout[0:i], stdLongWeekDay, layout[i+6:] 21 } 22 if !startsWithLowerCase(layout[i+3:]) { 23 return layout[0:i], stdWeekDay, layout[i+3:] 24 } 25 } 26 if layout[i:i+3] == "MST" { 27 return layout[0:i], stdTZ, layout[i+3:] 28 } 29 } 30 31 case '0': // 01, 02, 03, 04, 05, 06 32 if len(layout) >= i+2 && '1' <= layout[i+1] && layout[i+1] <= '6' { 33 return layout[0:i], std0x[layout[i+1]-'1'], layout[i+2:] 34 } 35 36 case '1': // 15, 1 37 if len(layout) >= i+2 && layout[i+1] == '5' { 38 return layout[0:i], stdHour, layout[i+2:] 39 } 40 return layout[0:i], stdNumMonth, layout[i+1:] 41 42 case '2': // 2006, 2 43 if len(layout) >= i+4 && layout[i:i+4] == "2006" { 44 return layout[0:i], stdLongYear, layout[i+4:] 45 } 46 return layout[0:i], stdDay, layout[i+1:] 47 48 case '_': // _2, _2006 49 if len(layout) >= i+2 && layout[i+1] == '2' { 50 //_2006 is really a literal _, followed by stdLongYear 51 if len(layout) >= i+5 && layout[i+1:i+5] == "2006" { 52 return layout[0 : i+1], stdLongYear, layout[i+5:] 53 } 54 return layout[0:i], stdUnderDay, layout[i+2:] 55 } 56 57 case '3': 58 return layout[0:i], stdHour12, layout[i+1:] 59 60 case '4': 61 return layout[0:i], stdMinute, layout[i+1:] 62 63 case '5': 64 return layout[0:i], stdSecond, layout[i+1:] 65 66 case 'P': // PM 67 if len(layout) >= i+2 && layout[i+1] == 'M' { 68 return layout[0:i], stdPM, layout[i+2:] 69 } 70 71 case 'p': // pm 72 if len(layout) >= i+2 && layout[i+1] == 'm' { 73 return layout[0:i], stdpm, layout[i+2:] 74 } 75 76 case '-': // -070000, -07:00:00, -0700, -07:00, -07 77 if len(layout) >= i+7 && layout[i:i+7] == "-070000" { 78 return layout[0:i], stdNumSecondsTz, layout[i+7:] 79 } 80 if len(layout) >= i+9 && layout[i:i+9] == "-07:00:00" { 81 return layout[0:i], stdNumColonSecondsTZ, layout[i+9:] 82 } 83 if len(layout) >= i+5 && layout[i:i+5] == "-0700" { 84 return layout[0:i], stdNumTZ, layout[i+5:] 85 } 86 if len(layout) >= i+6 && layout[i:i+6] == "-07:00" { 87 return layout[0:i], stdNumColonTZ, layout[i+6:] 88 } 89 if len(layout) >= i+3 && layout[i:i+3] == "-07" { 90 return layout[0:i], stdNumShortTZ, layout[i+3:] 91 } 92 93 case 'Z': // Z070000, Z07:00:00, Z0700, Z07:00, 94 if len(layout) >= i+7 && layout[i:i+7] == "Z070000" { 95 return layout[0:i], stdISO8601SecondsTZ, layout[i+7:] 96 } 97 if len(layout) >= i+9 && layout[i:i+9] == "Z07:00:00" { 98 return layout[0:i], stdISO8601ColonSecondsTZ, layout[i+9:] 99 } 100 if len(layout) >= i+5 && layout[i:i+5] == "Z0700" { 101 return layout[0:i], stdISO8601TZ, layout[i+5:] 102 } 103 if len(layout) >= i+6 && layout[i:i+6] == "Z07:00" { 104 return layout[0:i], stdISO8601ColonTZ, layout[i+6:] 105 } 106 if len(layout) >= i+3 && layout[i:i+3] == "Z07" { 107 return layout[0:i], stdISO8601ShortTZ, layout[i+3:] 108 } 109 110 case '.': // .000 or .999 - repeated digits for fractional seconds. 111 if i+1 < len(layout) && (layout[i+1] == '0' || layout[i+1] == '9') { 112 ch := layout[i+1] 113 j := i + 1 114 for j < len(layout) && layout[j] == ch { 115 j++ 116 } 117 // String of digits must end here - only fractional second is all digits. 118 if !isDigit(layout, j) { 119 std := stdFracSecond0 120 if layout[i+1] == '9' { 121 std = stdFracSecond9 122 } 123 std |= (j - (i + 1)) << stdArgShift 124 return layout[0:i], std, layout[j:] 125 } 126 } 127 } 128 } 129 return layout, 0, "" 130}
可以发现layout的所有代表年月日时分秒甚至时区的值都是互斥不相等的。
比如年份:短年份06,长年份2006,
月份:01,Jan,January
日:02,2,_2
时:15,3,03
分:04, 4
秒:05, 5
因为都不相等所以通过遍历layout就可以switch case解析出每个区块的意义和在字符串中的位置,这样输入对应格式的时间字符串就可以顺利解析出来。
这样layout也可以自定义,而且顺序任意,只要符合下列每个区块定义的规则即可, 代码中的注释就是规则写法:1const ( 2 _ = iota 3 stdLongMonth = iota + stdNeedDate // "January" 4 stdMonth // "Jan" 5 stdNumMonth // "1" 6 stdZeroMonth // "01" 7 stdLongWeekDay // "Monday" 8 stdWeekDay // "Mon" 9 stdDay // "2" 10 stdUnderDay // "_2" 11 stdZeroDay // "02" 12 stdHour = iota + stdNeedClock // "15" 13 stdHour12 // "3" 14 stdZeroHour12 // "03" 15 stdMinute // "4" 16 stdZeroMinute // "04" 17 stdSecond // "5" 18 stdZeroSecond // "05" 19 stdLongYear = iota + stdNeedDate // "2006" 20 stdYear // "06" 21 stdPM = iota + stdNeedClock // "PM" 22 stdpm // "pm" 23 stdTZ = iota // "MST" 24 stdISO8601TZ // "Z0700" // prints Z for UTC 25 stdISO8601SecondsTZ // "Z070000" 26 stdISO8601ShortTZ // "Z07" 27 stdISO8601ColonTZ // "Z07:00" // prints Z for UTC 28 stdISO8601ColonSecondsTZ // "Z07:00:00" 29 stdNumTZ // "-0700" // always numeric 30 stdNumSecondsTz // "-070000" 31 stdNumShortTZ // "-07" // always numeric 32 stdNumColonTZ // "-07:00" // always numeric 33 stdNumColonSecondsTZ // "-07:00:00" 34 stdFracSecond0 // ".0", ".00", ... , trailing zeros included 35 stdFracSecond9 // ".9", ".99", ..., trailing zeros omitted 36 37 stdNeedDate = 1 << 8 // need month, day, year 38 stdNeedClock = 2 << 8 // need hour, minute, second 39 stdArgShift = 16 // extra argument in high bits, above low stdArgShift 40 stdMask = 1<
时区:
时区使用:MST
时区偏移使用-0700
或者Z0700
等等。 下面是一个使用时区的例子,Z0700
比较特殊,当输入时间直接使用Z时就直接代表UTC时区。 1func testTimeParse() { 2 t, _ := time.Parse("2006-01-02 15:04:05 -0700 MST", "2018-09-20 15:39:06 +0800 CST") 3 fmt.Println(t) 4 t, _ = time.Parse("2006-01-02 15:04:05 -0700 MST", "2018-09-20 15:39:06 +0000 CST") 5 fmt.Println(t) 6 t, _ = time.Parse("2006-01-02 15:04:05 Z0700 MST", "2018-09-20 15:39:06 +0800 CST") 7 fmt.Println(t) 8 t, _ = time.Parse("2006-01-02 15:04:05 Z0700 MST", "2018-09-20 15:39:06 Z GMT") 9 fmt.Println(t) 10 t, _ = time.Parse("2006-01-02 15:04:05 Z0700 MST", "2018-09-20 15:39:06 +0000 GMT") 11 fmt.Println(t) 12} 13输出: 142018-09-20 15:39:06 +0800 CST 152018-09-20 15:39:06 +0000 CST 162018-09-20 15:39:06 +0800 CST 172018-09-20 15:39:06 +0000 UTC 182018-09-20 15:39:06 +0000 GMT
还有疑问的可以看看go自带的测试例子: Go/src/time/example_test.go
原文发布时间为:2018-09-23
本文作者:云上听风
本文来自云栖社区合作伙伴“”,了解相关信息可以关注“”。
转载地址:http://vxwuo.baihongyu.com/