在Go语言中将UTC转换为“本地”时间

40
如何将UTC时间转换为本地时间?
我已经创建了一张包含所需国家与UTC差异的地图。然后,我将该差异作为持续时间添加到当前时间(UTC)并打印结果,希望这就是特定国家的本地时间。
但是由于某些原因,结果是错误的。例如,在匈牙利,有一个小时的差异。您知道为什么会出现不正确的结果吗?
package main

import "fmt"
import "time"

func main() {

    m := make(map[string]string)
    m["Hungary"] = "+01.00h"

    offSet, err := time.ParseDuration(m["Hungary"])
    if err != nil {
        panic(err)
    }
    t := time.Now().UTC().Add(offSet)
    nice := t.Format("15:04")

    fmt.Println(nice)
}

具体是什么出了问题?你的代码输出的是UTC时间之后一小时的时钟时间。 - JimB
我期望得到匈牙利的本地时间,它是UTC+1,但是tz [1]报告的时间与go [1]生成的时间之间存在一个小时的差异,我认为这是正确的。http://www.timeanddate.com/worldclock/hungary/budapest - hey
游乐场的时间从2009-11-10 23:00:00 +0000 UTC开始。 - OneOfOne
1
@OneOfOne 我正在我的电脑上测试它,因此没有游乐场链接。 - hey
1
嘿,我明白你的意思,但你可能想将它们映射到time.Locations,而不是自定义持续时间。 - JimB
显示剩余2条评论
4个回答

57

请记住,游乐场的时间被设置为2009-11-10 23:00:00 +0000 UTC,因此它是正常工作的。

正确的方法是使用time.LoadLocation,以下是示例:

var countryTz = map[string]string{
    "Hungary": "Europe/Budapest",
    "Egypt":   "Africa/Cairo",
}

func timeIn(name string) time.Time {
    loc, err := time.LoadLocation(countryTz[name])
    if err != nil {
        panic(err)
    }
    return time.Now().In(loc)
}

func main() {
    utc := time.Now().UTC().Format("15:04")
    hun := timeIn("Hungary").Format("15:04")
    eg := timeIn("Egypt").Format("15:04")
    fmt.Println(utc, hun, eg)
}

它仍然显示与http://www.timeanddate.com/worldclock/hungary/budapest相比的错误时间。在我的机器上,它显示为23:21,但实际上似乎是00:24。我使用我的代码得到了同样的差异,因此提出了这个问题。 - hey
或者你可以使用 time.LoadLocation("Europe/Budapest") - JimB
2
@hey 虽然这似乎更普遍,但自己跟踪夏令时(特别是因为一些政府可能会在一年中改变几次,比如埃及)可能会很快变得混乱。 - OneOfOne
1
谢谢您展示了time.Time.In方法的使用。当我认为自己知道如何使用time包时,它及其目的完全逃过了我的注意。通常情况下,当我认为在golang包中遇到了一个错误时,实际上发现了我所做出的某种假设。 - WeakPointer
国家列表:https://zh.wikipedia.org/wiki/Tz数据库时区列表 - Ninh Pham
显示剩余5条评论

21

你的方法有误。一个国家可以有多个时区,例如美国和俄罗斯。由于夏令时(DST),一个时区可以有多个时间,例如匈牙利。匈牙利是UTC +1:00,夏令时为UTC+2:00。

如果要获取给定UTC时间的每个位置的本地时间,请使用IANA(tzdata)时区位置。例如,

package main

import (
    "fmt"
    "time"
)

func main() {
    utc := time.Now().UTC()
    fmt.Println(utc)
    local := utc
    location, err := time.LoadLocation("Europe/Budapest")
    if err == nil {
        local = local.In(location)
    }
    fmt.Println("UTC", utc.Format("15:04"), local.Location(), local.Format("15:04"))
    local = utc
    location, err = time.LoadLocation("America/Los_Angeles")
    if err == nil {
        local = local.In(location)
    }
    fmt.Println("UTC", utc.Format("15:04"), local.Location(), local.Format("15:04"))
}

输出:

2014-08-14 23:57:09.151377514 +0000 UTC
UTC 23:57 Europe/Budapest 01:57
UTC 23:57 America/Los_Angeles 16:57

参考文献:

IANA时区数据库

tz数据库

tz数据库时区

时区

匈牙利时间


3

不要费心去处理特定时区,使用位置“本地”。下面是一个完整而实用的本地时间和UTC时间转换示例:

package main

import (
    "fmt"
    "log"
    "time"
)

const (
    dateTimeFormat = "2006-01-02 15:04 MST"
    dateFormat    = "2006-01-02"
    timeFormat    = "15:04"
)

// A full cycle example of receiving local date and time,
// handing off to a database, retrieving as UTC, and formatting as local datetime
// This should be good in *any* timezone
func main() {
    // If using a form for entry, I strongly suggest a controlled format input like
    // <input type="date" ... > and <input type="time" ... >
    locallyEnteredDate := "2017-07-16"
    locallyEnteredTime := "14:00"

    // Build a time object from received fields (time objects include zone info)
    // We are assuming the code is running on a server that is in the same zone as the current user
    zone, _ := time.Now().Zone() // get the local zone
    dateTimeZ := locallyEnteredDate + " " + locallyEnteredTime + " " + zone
    dte, err := time.Parse(dateTimeFormat, dateTimeZ)
    if err != nil {
        log.Fatal("Error parsing entered datetime", err)
    }
    fmt.Println("dte:", dte) // dte is a legit time object
    // Perhaps we are saving this in a database.
    // A good database driver should save the time object as UTC in a time with zone field,
    // and return a time object with UTC as zone.

    // For the sake of this example, let's assume an object identical to `dte` is returned
    // dte := ReceiveFromDatabase()

    // Convert received date to local.
    // Note the use of the convenient "Local" location https://golang.org/pkg/time/#LoadLocation.
    localLoc, err := time.LoadLocation("Local")
    if err != nil {
        log.Fatal(`Failed to load location "Local"`)
    }
    localDateTime := dte.In(localLoc)

    fmt.Println("Date:", localDateTime.Format(dateFormat))
    fmt.Println("Time:", localDateTime.Format(timeFormat))
}

1
是的,我在这个答案中提供了额外的信息,但它将帮助新手全面了解处理日期、时间和时区的情况。 - Rohanthewiz
1
处理时间非常复杂。在需要处理时区时明确指出时区是一个好的实践。原帖提到了多个国家,很有可能需要考虑不同的时区。你对那些还在学习的人的敌意是不被欣赏的。 - Dynom

0
NB: 最好使用您的编辑器而不是go playground。
package main

import (
    "fmt"
    "time"
)

func main() {
    now := time.Now()

    location, _ := time.LoadLocation("UTC")
    fmt.Printf("UTC time is ------ %s\n", now.In(location))

    location, _ = time.LoadLocation("Europe/Berlin")
    fmt.Printf("Berlin time is ------ %s\n", now.In(location))

    location, _ = time.LoadLocation("Africa/Casablanca")
    fmt.Printf("Casablanca time is ------ %s\n", now.In(location))

    location, _ = time.LoadLocation("Asia/Dubai")
    fmt.Printf("Dubai time is ------ %s\n", now.In(location))
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接