我该如何存储Go的时间。位置在Postgres中?

时间:2019-05-03 08:59:21

标签: postgresql datetime go time timezone

在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)的结果是否足够好?

2 个答案:

答案 0 :(得分:2)

保存后,我将使用Time.Zone()获取区域名称和偏移量:

func (t Time) Zone() (name string, offset int)

然后,当从数据库中查询这样的时间戳时,可以使用time.Location来构建time.FixedZone()

func FixedZone(name string, offset int) *Location

然后使用Time.In()切换到此位置。

警告!:这将为您在同一时区中显示“似乎”的时间戳,但是如果您需要对其进行操作(例如为其添加天数),则结果可能不一样。这样做的原因是因为time.FixedZone()返回的时区具有固定的偏移量,例如对于daylight savings一无所知,而您保存的原始时间戳可能有一个time.Location了解这些事情。

这是这种偏差的一个例子。 3月有一个夏令时,因此我们将使用指向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个小时的偏移量。在添加后的时间戳中,甚至区域名称在现实生活中也会发生变化:从CETCEST,再一次,恢复的时间戳所在的区域也不知道这一点。

答案 1 :(得分:0)

更浪费且仍然容易出错,但仍然有效的解决方案是将原始时间戳记也以2019-05-2T17:24:37+01:00之类的ISO 8601格式存储在单独的列datetext中:

   Column   |           Type           | Collation | Nullable | Default
------------+--------------------------+-----------+----------+---------
 id         | uuid                     |           | not null |
 value      | numeric                  |           |          |
 date       | timestamp with time zone |           |          |
 datetext   | text                     |           |          |

然后使用date查询本机时间戳列的强度,并返回给用户datetext,这是最初发送的确切值。