Как в Go принято работать со временем и в каких форматах его хранить/передавать?

Ответ

Основным типом для работы со временем в Go является time.Time из стандартной библиотеки. Выбор формата для хранения или передачи зависит от конкретной задачи.

1. В коде (в памяти)

Всегда используйте time.Time. Это не просто число или строка, а полноценная структура, которая хранит время с наносекундной точностью и информацию о временной зоне (location). Это позволяет избежать ошибок при арифметических операциях и сравнениях.

2. В базах данных

  • Предпочтительный способ: Использовать нативные типы данных БД, такие как TIMESTAMP WITH TIME ZONE (PostgreSQL) или DATETIME (MySQL). Большинство драйверов баз данных (database/sql) умеют автоматически конвертировать time.Time в нужный формат и обратно.
  • Альтернатива: Хранить время как BIGINT, используя Unix Timestamp (количество секунд или миллисекунд с 1 января 1970 года). Недостаток: теряется информация о временной зоне.

3. При передаче по сети (JSON/API)

  • Стандарт: Строка в формате RFC3339 / ISO 8601. Это общепринятый стандарт, который однозначен, читаем человеком и включает информацию о смещении временной зоны. В Go для этого есть константа time.RFC3339.
    • Пример: "2023-10-27T10:00:00Z" (Z означает UTC).
  • Альтернатива: Число (JSON Number), представляющее Unix Timestamp.

Пример работы с форматами:

import (
    "fmt"
    "time"
)

func main() {
    // Рекомендуется работать с временем в UTC, чтобы избежать путаницы
    now := time.Now().UTC()

    // 1. Форматирование в строку для API (RFC3339)
    rfcString := now.Format(time.RFC3339)
    fmt.Println("Для API (RFC3339):", rfcString)

    // 2. Получение Unix timestamp для хранения в виде числа
    unixTimestamp := now.Unix()
    fmt.Println("Unix Timestamp (секунды):", unixTimestamp)

    // 3. Парсинг времени из строки
    parsedTime, err := time.Parse(time.RFC3339, rfcString)
    if err != nil {
        panic(err)
    }
    fmt.Println("Время совпадает после парсинга:", now.Equal(parsedTime))
}

Ответ 18+ 🔞

А, ну вот, опять про время в гоу, классика! Слушай, тут всё просто, как три копейки, но если накосячить — потом сам себя ненавидеть будешь, блядь.

Так, смотри. Внутри программы, в памяти, у тебя один король и бог — time.Time. Это не какая-то там строка или циферка, это, блядь, полноценный монстр, который помнит всё: и наносекунды, и в какой временной зоне это всё происходило. Пытаться хранить время как-то иначе в коде — это как ебашить молотком микроскопом, одни осколки и слезы получишь.

А вот когда дело доходит до сохранения или отправки куда-то — тут уже начинается цирк.

1. В базу данных лезет? Идеальный вариант — найти в своей БД родной тип для времени с часовым поясом (типа TIMESTAMP WITH TIME ZONE) и просто пихать туда time.Time. Драйвер сам, хитрая жопа, всё красиво преобразует. Если же хранить как Unix Timestamp (число секунд) — ну, можно, конечно, но тогда прощай, информация о временной зоне. Сам потом будешь гадать, что это было: по Москве или по UTC? Пиздец, а не загадка.

2. По сети JSON'ом шлёшь? Тут, блядь, без вариантов — только RFC3339. Это типа международный язык для времени, все его понимают. Выглядит как "2023-10-27T10:00:00Z". Буква Z — это не просто буква, это, ёпта, священный символ, означающий UTC. Используй константу time.RFC3339 и спи спокойно.

Вот, смотри, как это в коде выглядит, чтоб совсем понятно было:

import (
    "fmt"
    "time"
)

func main() {
    // Первое правило бойца: работай в UTC, чтобы не было потом волнения ебать
    now := time.Now().UTC()

    // Для API — делаем красивую строку по стандарту
    rfcString := now.Format(time.RFC3339)
    fmt.Println("Для API (RFC3339):", rfcString) // "2023-10-27T10:00:00Z"

    // Если вдруг тебе реально нужно число (хотя зачем?), то вот timestamp
    unixTimestamp := now.Unix()
    fmt.Println("Unix Timestamp (секунды):", unixTimestamp)

    // А вот как строку обратно в time.Time превратить
    parsedTime, err := time.Parse(time.RFC3339, rfcString)
    if err != nil {
        panic(err) // На этом этапе паниковать уже поздно, но ладно
    }
    fmt.Println("Время совпадает после парсинга:", now.Equal(parsedTime)) // true
}

Короче, запомни: в коде — time.Time, в базу — нативный тип БД, в JSON — RFC3339. Не выёбывайся, не придумывай свои форматы, и у тебя не будет проблем. А то знаю я этих кулибиных, которые время строкой в формате "дд.мм.гггг чч:мм" хранят, а потом охуевают, когда в другом часовом поясе всё ебётся. Не будь таким, ёпта.