【问题标题】:Error parsing time string with timezone as "GMT+0000"解析时区为“GMT+0000”的时间字符串时出错
【发布时间】:2020-07-29 09:11:03
【问题描述】:

我正在尝试使用 time.Parse 将“Tue Jun 11 2019 13:26:45 GMT+0000”与“Mon Jan 02 2006 15:04:05 MST-0700”解析为布局字符串,但我收到此错误parsing time "Tue Jun 11 2019 13:26:45 GMT+0000" as "Mon Jan 02 2006 15:04:05 MST-0700": cannot parse "" as "-0700".

我一直在将上述布局字符串用于其他偏移量,它工作正常。但是,我认为“+0000”不被认为是有效的偏移量或什么?任何建议都会有所帮助。

编辑:使用“Mon Jan 02 2006 15:04:05 GMT-0700”作为布局,我得到的输出为2019-06-11 13:26:45 +0000 +0000

【问题讨论】:

    标签: go


    【解决方案1】:

    编辑 2:根据reply in github issues,事实证明,在布局字符串中“MST-0700”实际上是两个时区值"MST" 和数字时区值"-0700",它优先于前者。而"GMT+08""GMT+00" 被视为一个整体的时区值,并与“MST”匹配。因此,"MST-0700" 将是 "GMT+08+0800"

    我很少接触时区相关的问题,但我个人认为这种行为令人困惑。


    原答案:

    这是 Go 的时间库的一个 错误 令人困惑的行为。虽然我不确定 GMT 时间格式的期望行为(因为我很少在时间格式字符串中看到类似 GMT+0800 的东西),但使 GMT+0800 有效但 GMT+0000 的代码逻辑没有意义。

    我在github上提交了一个issue:https://github.com/golang/go/issues/40472

    简而言之,Go 的时间库通过同时消耗布局字符串和值字符串来解析格式。目前在解析“GMT”时区时,如果以下值字符串(例如“+0800”或“+0000”)已签名且范围从-23+23,则它会消耗更多的值字符串.所以不仅"GMT+0000" 失败,而且"GMT+0030""GMT+08"(匹配到`"MST-07" 时)也会失败。

    这里是细节:在解析时区时,它检查布局字符串,发现“MST”,所以它尝试解析值字符串中的时区值,当时只有“GMT+0X00”(其中 X 为 '0' 或 '8')- 其他已被消耗(正确)。 Source

        case stdTZ:
            // Does it look like a time zone?
            if len(value) >= 3 && value[0:3] == "UTC" {
                z = UTC
                value = value[3:]
                break
            }
            n, ok := parseTimeZone(value)
            if !ok {
                err = errBad
                break
            }
            zoneName, value = value[:n], value[n:]
    

    这里一切都很好。 parseTimeZone 函数为 GMT 格式提供了一种特殊情况,原因对我来说并不明显,但 here is the code:

    // Special case 2: GMT may have an hour offset; treat it specially.
    if value[:3] == "GMT" {
        length = parseGMT(value)
        return length, true
    }
    

    然后parseGMT 代码就像this

    // parseGMT parses a GMT time zone. The input string is known to start "GMT".    
    // The function checks whether that is followed by a sign and a number in the
    // range -23 through +23 excluding zero.
    func parseGMT(value string) int {
        value = value[3:]
        if len(value) == 0 {
            return 3
        }
    
        return 3 + parseSignedOffset(value)
    }
    

    从评论中,我们已经可以感觉到问题:“+0800”不是从-23+23 范围内的数字,不包括前导零(而“+0000”)是。显然,这个函数试图指示(从返回值)消耗超过 3 个字节,即超过“GMT”。我们可以在code of parseSignedOffset确认。

    // parseSignedOffset parses a signed timezone offset (e.g. "+03" or "-04").
    // The function checks for a signed number in the range -23 through +23 excluding zero.
    // Returns length of the found offset string or 0 otherwise
    func parseSignedOffset(value string) int {
        sign := value[0]
        if sign != '-' && sign != '+' {
            return 0
        }
        x, rem, err := leadingInt(value[1:])
    
        // fail if nothing consumed by leadingInt
        if err != nil || value[1:] == rem {
            return 0
        }
        if sign == '-' {
            x = -x
        }
        if x < -23 || 23 < x {
            return 0
        }
        return len(value) - len(rem)
    }
    

    这里,leadingInt 是一个无趣的函数,它将 string 的前缀转换为 int64,以及字符串的其余部分。

    所以parseSignedOffset 做了文档宣传的事情:它解析值字符串并查看它是否在-23+23 的范围内,但只是表面值:

    1. 它将+0800 视为800,大于+23,因此返回0。结果parseGMT(和parseTimeZone)返回3,所以Parse函数这次只消耗了3个字节,留下"+0800"匹配"-0700",所以解析正确。

    2. 它将+0000 视为0,该范围内的有效值,因此它返回5,意味着“+0000”是时区的一部分,因此parseGMT(和parseTimeZone)返回8 ,使Parse 函数消耗整个字符串,使"""-0700" 匹配,从而导致错误。

    编辑: 在格式字符串中使用 GMT 并获得“正确”值是因为格式字符串中的“GMT”不被视为时区,而是格式的一部分(就像字符串中的空格),“GMT”时区与默认时区(“UTC”)相同。 你可以time.Parse("Mon Jan 02 2006 15:04:05 XYZ-0700", "Tue Jun 11 2019 13:26:45 XYZ+0800") 不会出错。

    【讨论】:

      猜你喜欢
      • 2021-08-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-11-15
      • 1970-01-01
      • 2013-11-04
      • 1970-01-01
      相关资源
      最近更新 更多