簡體   English   中英

為什么 Go 的 time.Parse() 不解析時區標識符?

[英]Why doesn't Go's time.Parse() parse the timezone identifier?

考慮以下代碼:

package main

import (
    "time"
    "fmt"
)

const (
    format = "2006 01 02 15:04 MST"

    date = "2018 08 01 12:00 EDT"
)

func main() {
    aloc, _ := time.LoadLocation("America/New_York")
    eloc, _ := time.LoadLocation("Europe/Berlin")
    tn, _ := time.Parse(format, date)
    tl, _ := time.ParseInLocation(format, date, aloc)

    fmt.Println(tn) // Says +0000 despite EDT being -0400
    fmt.Println(tn.In(eloc)) // Expect 18:00, but get 14:00
    fmt.Println(tl) // Correctly -0400
    fmt.Println(tl.In(eloc)) // Correctly 18:00
}

您也可以在Go Playground上試用。

當我運行它時,我得到了這個結果(在我自己的系統上和通過 Playground):

2018-08-01 12:00:00 +0000 EDT
2018-08-01 14:00:00 +0200 CEST
2018-08-01 12:00:00 -0400 EDT
2018-08-01 18:00:00 +0200 CEST

我原以為第一行和第三行是一樣的,第二行和第四行也是一樣的。

在我看來,Go 的時間庫不會解析我在日期字符串中寫入的“EDT”時區標識符,盡管它是格式的一部分。

我自己的系統(Fedora 26)也將 EST/EDT 識別為時區:

$ TZ='America/New_York' date 080112002018
Wed  1 Aug 12:00:00 EDT 2018

當然,如您所見,我可以使用ParseInLocation()強制解決這個問題,但這只有在我事先知道時區時才有用。 否則我需要自己將日期字符串的“EDT”部分解析為“America/New_York”。

或者我錯過了什么?

引用時間#Parse

當解析帶有像MST這樣的時區縮寫的時間時,如果時區縮寫在當前位置有定義的偏移量,則使用該偏移量。

這里的關鍵是“當前位置”。 對我來說,這就是CST 使用它按預期工作:

package main
import "time"

func main() {
   t, e := time.Parse(time.RFC1123, "Wed, 01 Aug 2018 12:00:00 CST")
   if e != nil {
      panic(e)
   }
   s := t.String()
   println(s == "2018-08-01 13:00:00 -0500 CDT")
}

如果時區縮寫未知,Parse 會將時間記錄為處於具有給定時區縮寫和零偏移量的虛構位置。

package main
import "time"

func main() {
   t, e := time.Parse(time.RFC1123, "Wed, 01 Aug 2018 12:00:00 EDT")
   if e != nil {
      panic(e)
   }
   s := t.String()
   println(s == "2018-08-01 12:00:00 +0000 EDT")
}

為避免此類問題,請首選使用數字區域偏移量的時間布局,或使用 ParseInLocation。

package main
import "time"

func main() {
   { // example 1
      t, e := time.Parse(time.RFC1123Z, "Wed, 01 Aug 2018 12:00:00 -0400")
      if e != nil {
         panic(e)
      }
      s := t.String()
      println(s == "2018-08-01 12:00:00 -0400 -0400")
   }
   { // example 2
      ny, e := time.LoadLocation("America/New_York")
      if e != nil {
         panic(e)
      }
      t, e := time.ParseInLocation(time.RFC1123, "Wed, 01 Aug 2018 12:00:00 EDT", ny)
      if e != nil {
         panic(e)
      }
      s := t.String()
      println(s == "2018-08-01 12:00:00 -0400 EDT")
   }
}

一個簡單的調試運行表明,這一切都歸結為這個函數go/1.10/libexec/src/time/zoneinfo.go:226

func (l *Location) lookupName(name string, unix int64) (offset int, ok bool) {
    l = l.get()

    // First try for a zone with the right name that was actually
    // in effect at the given time. (In Sydney, Australia, both standard
    // and daylight-savings time are abbreviated "EST". Using the
    // offset helps us pick the right one for the given time.
    // It's not perfect: during the backward transition we might pick
    // either one.)
    for i := range l.zone {
        zone := &l.zone[i]
        if zone.name == name {
            nam, offset, _, _, _ := l.lookup(unix - int64(zone.offset))
            if nam == zone.name {
                return offset, true
            }
        }
    }

    // Otherwise fall back to an ordinary name match.
    for i := range l.zone {
        zone := &l.zone[i]
        if zone.name == name {
            return zone.offset, true
        }
    }

    // Otherwise, give up.
    return
}

在我的 OSX 上(我在蘇黎世,所以現在是CET ),對l.get()的調用返回在區域切片中包含 4 個值的對象上,再次是CETCESTCETCEST 最重要的是, GMTUTC事先經過特殊處理。 所有其他區域對我來說都是“未知”的,包括 EDT。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM