在Postgres中我存储用户给我的数据:
Column | Type | Collation | Nullable | Default
------------+--------------------------+-----------+----------+---------
id | uuid | | not null |
value | numeric | | |
date | timestamp with time zone | | |
现在,我介绍了维护生成数据的原始时区的要求。timestamp with timezone
已归一化为数据库的时区,而原始时区丢失,因此我必须手动从归一化的时区重新恢复date
,然后再将其提供给用户。
大多数解决方案都建议在表中添加额外的列,并将原始时区信息与时间戳一起存储:
: Column | Type | Collation | Nullable | Default
------------+--------------------------+-----------+----------+---------
id | uuid | | not null |
value | numeric | | |
date | timestamp with time zone | | |
tz | text | | |
因此,鉴于我正在使用GO,我应该从time.Time
提取哪些信息以存储在tz
中,以获取最精确和无缝的修复?
date.Location().String()
似乎不正确,因为它可能返回相对的值 Local
。
以及如何将信息从tz
恢复到time.Time
?
time.LoadLocation(tz)
的结果足够好吗?
保存后,我将使用 Time.Zone()
获得区域名称并偏移:
func (t Time) Zone() (name string, offset int)
然后,当从数据库中查询这样的时间戳时,您可以使用time.FixedZone()
构造time.Location
:
func FixedZone(name string, offset int) *Location
并使用Time.In()
。
谨慎词!这将使您在同一时区域恢复使用"貌似"的时间戳,但是如果您需要对其进行应用程序(例如添加天数(,则结果可能不一样。原因是因为time.FixedZone()
返回一个带有固定偏移的时区,例如,对于日光节省一无所知,而您保存的原始时间戳可能具有time.Location
,这确实知道这些事情。
这是这样偏差的一个例子。三月份有一个日光节省的一天,所以我们将使用时间戳指向3月1日,并为其添加1个月,从而导致时间戳在节省日光之后。
cet, err := time.LoadLocation("CET")
if err != nil {
panic(err)
}
t11 := time.Date(2019, time.March, 1, 12, 0, 0, 0, cet)
t12 := t11.AddDate(0, 1, 0)
fmt.Println(t11, t12)
name, offset := t11.Zone()
cet2 := time.FixedZone(name, offset)
t21 := t11.UTC().In(cet2)
t22 := t21.AddDate(0, 1, 0)
fmt.Println(t21, t22)
now := time.Date(2019, time.April, 2, 0, 0, 0, 0, time.UTC)
fmt.Println("Time since t11:", now.Sub(t11))
fmt.Println("Time since t21:", now.Sub(t21))
fmt.Println("Time since t12:", now.Sub(t12))
fmt.Println("Time since t22:", now.Sub(t22))
这将输出(在Go Playground上尝试(:
2019-03-01 12:00:00 +0100 CET 2019-04-01 12:00:00 +0200 CEST
2019-03-01 12:00:00 +0100 CET 2019-04-01 12:00:00 +0100 CET
Time since t11: 757h0m0s
Time since t21: 757h0m0s
Time since t12: 14h0m0s
Time since t22: 13h0m0s
您可以看到,1个月后的输出时间相同,但是区域偏移量不同,因此它们在时间上表示不同的时间(通过显示任意时间的时间差,可以证明这一点(。原件有2小时的偏移,因为它知道我们跳过1个月内发生的日光节省,而"恢复"时间戳的区域不知道这一点,因此结果具有相同的1小时偏移量。在添加后的时间戳中,即使区域名称在现实生活中也会变化:从CET
到CEST
,再一次,还原的时间戳区域也不知道。
更浪费,仍然容易出错,但仍然有效的解决方案还将在ISO 8601格式中存储原始的时间戳,例如2019-05-2T17:24:37+01:00
,例如datetext
:
Column | Type | Collation | Nullable | Default
------------+--------------------------+-----------+----------+---------
id | uuid | | not null |
value | numeric | | |
date | timestamp with time zone | | |
datetext | text | | |
然后,使用date
查询本机时间戳列的强度,然后返回到用户datetext
,这是最初发送的确切值。