go - 解析时区为 “GMT+0000”的时间字符串时出错

标签 go

我正在尝试使用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

最佳答案

编辑2:根据reply in github issues,事实证明,在布局字符串“MST-0700”中实际上是两个时区值"MST"和数字时区值"-0700",它们优先于前者。并且"GMT+08""GMT+00"整体上被视为时区值,并且与“MST”匹配。因此它将是"GMT+08+0800""MST-0700"
我很少涉及与时区相关的问题,但我个人认为这种行为令人困惑。

原始答案:
这是一个混淆Go时间库行为的错误。虽然我不确定GMT时间格式的预期行为(因为我很少在时间格式字符串中看到GMT+0800之类的东西),但是使GMT+0800有效但GMT+0000的代码逻辑没有意义。
我已经在github上提交了一个问题: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的范围内,但实际值是:
  • 它将+0800视为800,大于+23,因此返回0。结果,parseGMT(和parseTimeZone)返回3,因此这次Parse函数仅消耗3个字节,并保留"+0800""-0700"匹配,因此可以正确解析。
  • 它将+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")而不会出现错误。

    关于go - 解析时区为 “GMT+0000”的时间字符串时出错,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63150212/

    相关文章:

    go - 静态链接go程序与GNU readline

    sockets - go 中的套接字回显服务器

    listen 和 serve 的 goroutine 会阻塞主进程的执行,永远不会到达以下用于通信的选择状态

    go - Terraform Schema Elem 支持多种类型吗?

    json - 如何反序列化混合类型的列表?

    go - 谁能帮忙解析一下HCL?

    mysql - 使用下划线从 golang 结构体访问 mysql 字段

    oracle - 使用 Oracle 使用 INSERT 查询返回值

    go - 向 ResponseWriter 添加 header

    go - 并行获取多个字段的模式