Как я должен хранить время Go. Расположение в Postgres? - PullRequest
3 голосов
/ 03 мая 2019

В 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 ]

2 голосов
/ 03 мая 2019

После сохранения я получил бы имя зоны и смещение, используя 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() возвращает часовой пояс с фиксированным смещением, который, например, ничего не знает о переходе на летнее время , тогда как исходная сохраненная вами временная метка может иметь time.Location, который знаетоб этих вещах.

Вот пример такого отклонения.В марте наступает переход на летнее время, поэтому мы будем использовать метку времени, указывающую на 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, и опять же, зона восстановленной метки времени также не знает об этом.

0 голосов
/ 03 мая 2019

Более расточительным и все еще подверженным ошибкам, но все еще действующим решением будет также сохранение исходной временной метки в формате 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, которое является точным значением, которое было первоначально отправлено.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...