【问题标题】:Why is time.Parse not using the timezone?为什么 time.Parse 不使用时区?
【发布时间】:2018-10-21 14:18:14
【问题描述】:

为什么time.Parse 不使用时区信息?它应该为不同的时区返回不同的时间。

代码

package main

import (
    "fmt"
    "time"
)

func main() {
    t, err := time.Parse("2006-01-02 MST", "2018-05-11 IST")
    if err != nil {
        return
    }
    t2, err := time.Parse("2006-01-02 MST", "2018-05-11 UTC")
    if err != nil {
        return
    }
    fmt.Println(t.Unix())
    fmt.Println(t2.Unix())
}

输出:

1525996800
1525996800

【问题讨论】:

    标签: parsing go time timezone


    【解决方案1】:

    对问题本身的一些解释:

    这 2 个时间戳 2018-05-11 IST2018-05-11 UTC 不指定同一时刻,因为 IST 与 UTC 有不同的偏移量:印度标准时间 (IST) 比协调世界时 (UTC) 早 5:30 小时。

    并且Time.Unix() 返回自参考时间(UTC 时间 1970 年 1 月 1 日)以来已过去的秒数。这意味着输出应该不同!

    在 PlayGround 上运行您的代码确实会给出错误的结果 (link)。

    这是一个时区相关的问题。如果您尝试加载 IST 时区:

    loc, err := time.LoadLocation("IST")
    fmt.Println(loc, err)
    

    输出:

    UTC cannot find IST in zip file /usr/local/go/lib/time/zoneinfo.zip
    

    而“IST”之所以不受支持,是因为它不明确。这可能意味着印度、爱尔兰、以色列等时区,它们有不同的时区偏移和规则。

    time.Parse() 的文档指出

    如果区域缩写未知,Parse 将时间记录为在具有给定区域缩写和零偏移量的虚构位置。

    因此,parse.Parse() 返回的 time.time 与 UTC 时区一样具有 0 偏移量,因此它将产生相同的“unix 时间”(Time.Unix() 将返回相同的值)。

    但是在本地(使用我的 CET)时区运行它会给出不同的正确结果:

    t, err := time.Parse("2006-01-02 MST", "2018-05-11 CET")
    if err != nil {
        panic(err)
    }
    t2, err := time.Parse("2006-01-02 MST", "2018-05-11 UTC")
    if err != nil {
        panic(err)
    }
    fmt.Println(t)
    fmt.Println(t2)
    
    fmt.Println(t.Unix())
    fmt.Println(t2.Unix())
    

    输出:

    2018-05-11 01:00:00 +0200 CEST
    2018-05-11 00:00:00 +0000 UTC
    1525993200
    1525996800
    

    time.Parse() 的文档中有关于使用区域缩写解析时间的说法:

    当使用像 MST 这样的区域缩写解析时间时,如果区域缩写在当前位置有定义的偏移量,则使用该偏移量。时区缩写“UTC”被识别为 UTC,与位置无关。如果区域缩写未知,Parse 将时间记录为在具有给定区域缩写和零偏移的虚构位置。这种选择意味着可以使用相同的布局无损地解析和重新格式化这样的时间,但表示中使用的确切时刻将因实际区域偏移量而异。为避免此类问题,请首选使用数字区域偏移的时间布局,或使用 ParseInLocation。

    文档建议使用带有数字区域偏移的布局进行解析,如下所示:

    t, err := time.Parse("2006-01-02 -0700", "2018-05-11 +0530")
    if err != nil {
        panic(err)
    }
    

    然后输出(在Go Playground上试试):

    2018-05-11 00:00:00 +0530 +0530
    2018-05-11 00:00:00 +0000 UTC
    1525977000
    1525996800
    

    另一种选择是使用time.FixedZone()自己构造IST,并使用time.ParseInLocation(),通过我们的手册IST位置:

    ist := time.FixedZone("IST", 330*60) // +5:30
    t, err := time.ParseInLocation("2006-01-02 MST", "2018-05-11 IST", ist)
    

    输出将是(在Go Playground 上尝试):

    2018-05-11 00:00:00 +0530 IST
    2018-05-11 00:00:00 +0000 UTC
    1525977000
    1525996800
    

    另一种选择是按加尔各答市加载印度 IST 区域:

    loc, err := time.LoadLocation("Asia/Kolkata")
    if err != nil {
        panic(err)
    }
    

    这将产生相同的输出。试试Go Playground

    【讨论】:

    • "IST" 不明确。可能是印度、爱尔兰、以色列,它们都是不同的时区。 Go 不可能知道其中的意思;但如果应用程序开发人员这样做,那么您可以使用time.ParseInLocation()
    • @MichaelHampton 感谢您提供的信息,已纳入答案中。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-08
    • 1970-01-01
    • 2023-03-14
    • 1970-01-01
    • 2011-12-17
    相关资源
    最近更新 更多