首页 > 解决方案 > 为什么 Go time.Format 会根据时区返回不同的值?

问题描述

我认为 Go time.Format应该根据布局格式化时间。但似乎它根据时区信息返回不同的值。

package main

import (
    "fmt"
    "time"
)

func main() {

    formats := []string{
        time.RFC3339,
    }
    times := []string{
        "2020-03-08T01:59:50-08:00",
        "2020-03-08T01:59:59-08:00", //daylight saving starts 1 second later
        "2020-03-08T05:59:59-08:00",
    }
    for _, f := range formats {
        for _, t := range times {
            fmt.Printf("Format: %s\n", f)
            t, err := time.Parse(f, t)
            if err != nil {
                panic(err)
            }
            fmt.Printf("unix: %d\n", t.UnixNano())
            fmt.Printf("time: %s\n", t.Format(f))
            t = t.Add(time.Second)
            fmt.Printf("time + 1s: %s\n", t.Format(f))
        }
    }
}

运行输出:

➜ go version
go version go1.15 darwin/amd64
➜ TZ=UTC go run main.go
Format: 2006-01-02T15:04:05Z07:00
unix: 1583661590000000000
time: 2020-03-08T01:59:50-08:00
time + 1s: 2020-03-08T01:59:51-08:00
Format: 2006-01-02T15:04:05Z07:00
unix: 1583661599000000000
time: 2020-03-08T01:59:59-08:00
time + 1s: 2020-03-08T02:00:00-08:00 (a: this is not expected)
unix: 1583675999000000000
time: 2020-03-08T05:59:59-08:00
time + 1s: 2020-03-08T06:00:00-08:00
➜ TZ=America/Los_Angeles go run main.go
Format: 2006-01-02T15:04:05Z07:00
unix: 1583661590000000000
time: 2020-03-08T01:59:50-08:00
time + 1s: 2020-03-08T01:59:51-08:00
Format: 2006-01-02T15:04:05Z07:00 
unix: 1583661599000000000
time: 2020-03-08T01:59:59-08:00
time + 1s: 2020-03-08T03:00:00-07:00 (b: this is expected)
Format: 2006-01-02T15:04:05Z07:00
unix: 1583675999000000000
time: 2020-03-08T05:59:59-08:00
time + 1s: 2020-03-08T06:00:00-08:00 (c: this contradicts with the b)

标签: gotimetimezone

解决方案


该行为已记录在案。的输出time.Format只是一个结果,而不是混淆的来源——即time.Parse

函数解析

在解析具有 -0700 之类的区域偏移量的时间时,如果偏移量对应于当前位置 (Local) 使用的时区,则 Parse 在返回的时间中使用该位置和区域。否则,它将时间记录为在一个虚构的位置,时间固定在给定的区域偏移。

进一步的解释可以在 Location 类型下找到:

Local 表示系统的本地时区。在 Unix 系统上,Local 查询 TZ 环境变量以查找要使用的时区。无 TZ 表示使用系统默认的 /etc/localtime。TZ="" 表示使用 UTC。TZ="foo" 表示使用系统时区目录中的文件 foo。

基本上,go 的解析器尝试从 UTC 偏移量推断时区。如果解析后的 UTC 偏移量与TZ环境变量设置的时区匹配,则在返回的time. 在处理日期和时间时,简单似乎总是结束。


推荐阅读