Ответ
Для работы со временем в Go следует использовать стандартный тип time.Time
. Он изначально спроектирован для корректной работы с часовыми поясами.
Ключевые принципы:
Храните время в UTC. Внутри вашего приложения, в базах данных и при передаче через API всегда используйте универсальное координированное время (UTC). Это единый стандарт, который исключает путаницу с часовыми поясами и летним/зимним временем.
Используйте
time.Location
для работы с поясами. Для представления конкретного часового пояса (например,"Europe/Moscow"
) используйте объекты*time.Location
. Они содержат все правила перехода на летнее время для данной зоны.Конвертируйте в локальное время только для отображения. Преобразование из UTC в локальный часовой пояс пользователя должно происходить на самом последнем этапе — непосредственно перед отображением данных пользователю.
Для сериализации используйте
time.RFC3339
. При передаче времени в текстовом виде (например, в JSON API) используйте форматtime.RFC3339
("2006-01-02T15:04:05Z07:00"
). Он является стандартом ISO 8601 и содержит информацию о смещении относительно UTC, что делает его однозначным.В базе данных используйте тип, поддерживающий часовые пояса, например
TIMESTAMP WITH TIME ZONE
(илиTIMESTAMPTZ
) в PostgreSQL.
Пример:
package main
import (
"fmt"
"time"
)
func main() {
// 1. Получаем текущее время. Оно уже содержит информацию о системной зоне.
now := time.Now()
fmt.Printf("Текущее время: %sn", now)
// 2. Конвертируем в UTC для хранения или передачи.
utcTime := now.UTC()
fmt.Printf("Время в UTC: %sn", utcTime)
// 3. Для отображения пользователю в Москве, загружаем нужную локацию.
moscowLocation, err := time.LoadLocation("Europe/Moscow")
if err != nil {
panic(err)
}
moscowTime := utcTime.In(moscowLocation)
fmt.Printf("Время в Москве: %sn", moscowTime)
// 4. Сериализация в стандартный формат для API.
fmt.Printf("Формат RFC3339: %sn", now.Format(time.RFC3339))
}