如何在不重新解释现有时间戳的情况下设置时区?

huangapple go评论100阅读模式
英文:

How to set a timezone to an existing timestamp without reinterpreting it?

问题

我正在解析用户发送的时间戳。这些时间戳是相对于某个地点的本地时间,但源字符串没有指定地点。在服务器端,我需要查找该地点的时区,并将时间转换为该时区,同时不改变其显示值。

我知道可以通过以下方式在不同的地点获取等效时间:

package main

import (
	"fmt"
	"time"
)

func main() {
	myTime := time.Now()
	fmt.Println(myTime.Format(time.RFC3339))
	
	loc, err := time.LoadLocation("America/New_York")
	if err != nil {
		panic(err)
	}
	
	fmt.Println(myTime.In(loc).Format(time.RFC3339))
}

这将输出:

2009-11-10T23:00:00Z
2009-11-10T18:00:00-05:00

这不是我想要的结果。

我正在尝试找到一种设置时区的方法,例如将时区设置为America/New_York,这样我应该得到2009-11-10T23:00:00-05:00,即原始的本地时间,但应用了纽约的偏移量。

在Go语言中,我该如何实现这个目标?

英文:

I'm parsing timestamps sent by users. The timestamps are local to a location but the source string doesn't specify it. Server-side I'm looking up the timezone of the location and need to shift the time into that timezone, without changing its display value.

I know I can do this to give me the equivalent time at a different location:

package main

import (
	"fmt"
	"time"
)

func main() {
	myTime := time.Now()
	fmt.Println(myTime.Format(time.RFC3339))
	
	loc, err := time.LoadLocation("America/New_York")
	if err != nil {
		panic(err)
	}
	
	fmt.Println(myTime.In(loc).Format(time.RFC3339))
}

This just prints:

2009-11-10T23:00:00Z
2009-11-10T18:00:00-05:00

Which isn't what I want.

I'm trying to find a way of setting the timezone to e.g. America/New_York, so I should get e.g. 2009-11-10T23:00:00-05:00, which is the original local time, but with the New York offset applied.

How can I do this in Go?

答案1

得分: 3

混淆的原因在于直观地想到的 API In 会将相同的时间点解释为不同的时区。因此,当你打印它时,显示的结果并不是你想要的。

要将时间戳的时区设置为新的时区,同时保持相同的显示值,你可以使用 time.Date 构造具有与原始时间戳相同值和新位置的新时间戳:

t := time.Date(myTime.Year(), myTime.Month(), myTime.Day(), myTime.Hour(), myTime.Minute(), myTime.Second(), myTime.Nanosecond(), loc)
// 在 playground 中显示为 2009-11-10T23:00:00-05:00

另一种选择是将时间实例设置为新的时区,然后使用 Zone() 获取偏移量,然后从本地化时间中减去偏移量的秒数值。

package main

import (
    "fmt"
    "time"
)

func main() {
    myTime := time.Now()
    fmt.Println(myTime.Format(time.RFC3339))
    
    loc, err := time.LoadLocation("America/New_York")
    if err != nil {
        panic(err)
    }

    locTime := myTime.In(loc)
    _, zoneOffset := locTime.Zone()
    
    inZoneTime := locTime.Add(-time.Duration(zoneOffset) * time.Second)

    // 处理夏令时转换
    if inZoneTime.IsDST() {
		inZoneTime = inZoneTime.Add(1*time.Hour)
	}
    
    fmt.Println(inZoneTime.Format(time.RFC3339))
    // 2009-11-10T23:00:00-05:00
}

要在本地机器上测试今天的夏令时转换(假设你所在的国家没有夏令时,就像我一样),你可以将位置更改为夏令时生效的地方,例如 Australia/Canberra

使用没有夏令时的 time.Now() 作为输入,在 Australia/Canberra 时区中,上述程序将打印以下结果:

2021-11-12T13:27:33+01:00
is DST: true
2021-11-12T13:27:33+11:00

Playground: https://play.golang.org/p/5qy2tOcIMwn

英文:

The confusion comes from the fact that the API that intuitively comes to mind In simply interprets the same point in time as if it were in a different time zone. So when you print it, the display isn't what you want.

To set the time zone to a timestamp while keeping the same display value you can simply construct the new timestamp with time.Date with the same values as the original timestamp and the new location:

t := time.Date(myTime.Year(), myTime.Month(), myTime.Day(), myTime.Hour(), myTime.Minute(), myTime.Second(), myTime.Nanosecond(), loc)
// 2009-11-10T23:00:00-05:00 in the playground

<hr>

Another option is to set the time instance to the new time zone, then use Zone() to get the offset, and then subtract its value in seconds from the localized time.

package main

import (
    &quot;fmt&quot;
    &quot;time&quot;
)

func main() {
    myTime := time.Now()
    fmt.Println(myTime.Format(time.RFC3339))
    
    loc, err := time.LoadLocation(&quot;America/New_York&quot;)
    if err != nil {
        panic(err)
    }

    locTime := myTime.In(loc)
    _, zoneOffset := locTime.Zone()
    
    inZoneTime := locTime.Add(-time.Duration(zoneOffset) * time.Second)

    // handle DST transitions
    if inZoneTime.IsDST() {
		inZoneTime = inZoneTime.Add(1*time.Hour)
	}
    
    fmt.Println(inZoneTime.Format(time.RFC3339))
    // 2009-11-10T23:00:00-05:00
}

To test the DST transition in your local machine today (assuming you are in a non-DST country, as I am) you can change the location to a place where DST is active, e.g. Australia/Canberra.

With an input of time.Now() without DST into Australia/Canberra, the above program prints the following:

2021-11-12T13:27:33+01:00
is DST: true
2021-11-12T13:27:33+11:00

Playground: https://play.golang.org/p/5qy2tOcIMwn

huangapple
  • 本文由 发表于 2021年11月12日 18:20:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/69941303.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定