• 企业400电话
  • 微网小程序
  • AI电话机器人
  • 电商代运营
  • 全 部 栏 目

    企业400电话 网络优化推广 AI电话机器人 呼叫中心 网站建设 商标✡知产 微网小程序 电商运营 彩铃•短信 增值拓展业务
    golang的时区和神奇的time.Parse的使用方法

    时区

    先写一段测试代码:

    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))
    
     //指定时区
     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

    从以上代码的测试结果可以得出几点:

    神奇的time.Parse

    一开始使用time.Parse时很不习惯,因为非常奇怪的layout参数。
    除了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"

    网上基本上都在传说这个日子是golang项目开始创建的时间,为了纪念生日才这样设计,其实这真是无稽之谈瞎扯淡。
    网上文章没有找到说的比较清楚的,幸好有源码,打开time.Parse的源码看了一下,发现这个设计很好很科学。
    解析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, ""
    }
    
    

    可以发现layout的所有代表年月日时分秒甚至时区的值都是互斥不相等的。

    比如年份:短年份06,长年份2006,
    月份:01,Jan,January
    日:02,2,_2
    时:15,3,03
    分:04, 4
    秒:05, 5

    因为都不相等所以通过遍历layout就可以switch case解析出每个区块的意义和在字符串中的位置,这样输入对应格式的时间字符串就可以顺利解析出来。
    这样layout也可以自定义,而且顺序任意,只要符合下列每个区块定义的规则即可,
    代码中的注释就是规则写法:

    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  = 1stdArgShift - 1 // mask out argument
    )
    
    

    时区:

    时区使用:MST
    时区偏移使用-0700或者Z0700等等。
    下面是一个使用时区的例子,Z0700比较特殊,当输入时间直接使用Z时就直接代表UTC时区。

    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自带的测试例子:Go/src/time/example_test.go

    到此这篇关于golang的时区和神奇的time.Parse的使用方法的文章就介绍到这了,更多相关golang的时区和time.Parse内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

    您可能感兴趣的文章:
    • golang 定时任务方面time.Sleep和time.Tick的优劣对比分析
    • 解决Golang time.Parse和time.Format的时区问题
    • 解决golang时间字符串转time.Time的坑
    • 对Golang中的runtime.Caller使用说明
    • Golang中的time.Duration类型用法说明
    • golang time包做时间转换操作
    • golang xorm及time.Time自定义解决json日期格式的问题
    • golang time常用方法详解
    上一篇:用Go+WebSocket快速实现一个chat服务
    下一篇:Go Gin实现文件上传下载的示例代码
  • 相关文章
  • 

    © 2016-2020 巨人网络通讯 版权所有

    《增值电信业务经营许可证》 苏ICP备15040257号-8

    golang的时区和神奇的time.Parse的使用方法 golang,的,时区,和,神奇,time.Parse,