Хранение дат в базе данных

Хранение временных значений в реляционных и нереляционных системах опирается на строго определённые форматы, обеспечивающие однозначность интерпретации. Основная сложность заключается не в самой дате, а в контексте её хранения: временная зона, точность, способ сериализации и поведение при миграциях между системами.

На уровне баз данных применяются несколько подходов:

  • TIMESTAMP / DATETIME — нативные типы СУБД
  • Unix timestamp — число секунд или миллисекунд с начала эпохи UTC
  • ISO 8601 строка — текстовое представление даты и времени
  • JSON-структуры — при документоориентированном хранении

Каждый подход имеет ограничения, которые проявляются при масштабировании системы, работе с часовыми поясами и интеграции с JavaScript-слоем приложения.


Unix timestamp как универсальная форма

Unix timestamp представляет собой целое число, фиксирующее количество секунд или миллисекунд, прошедших с 1 января 1970 года (UTC).

В JavaScript и Day.js чаще используется миллисекундная точность:

const now = Date.now(); // 1716451200000

В Day.js преобразование выполняется напрямую:

import dayjs fr om 'dayjs';

const timestamp = dayjs().valueOf();

Преимущества хранения в виде timestamp:

  • отсутствие проблем с часовыми поясами
  • компактность представления
  • высокая скорость сортировки и сравнения
  • единая точка истины (UTC)

Недостатки:

  • отсутствие человекочитаемости
  • необходимость преобразования для отображения
  • риск ошибок при работе с секундами vs миллисекундами

ISO 8601 как стандарт сериализации

ISO 8601 представляет собой строковый стандарт:

2026-05-23T14:30:00.000Z

Day.js по умолчанию поддерживает преобразование в этот формат:

const iso = dayjs().toISOString();

ISO-строки часто используются в API и логировании, так как они:

  • однозначно интерпретируются всеми системами
  • включают информацию о временной зоне (или её отсутствии)
  • совместимы с JSON без дополнительной сериализации

При хранении в базе данных ISO-формат используется в основном в NoSQL-системах или как промежуточный слой между сервисами.


Работа с временными зонами

Основная проблема хранения дат заключается в несогласованности локального времени и UTC. Day.js решает это через плагины:

import utc from 'dayjs/plugin/utc';
import timezone from 'dayjs/plugin/timezone';

dayjs.extend(utc);
dayjs.extend(timezone);

Пример конвертации:

const mskTime = dayjs().tz('Europe/Moscow').format();

Ключевой принцип хранения

В базе данных практически всегда сохраняется UTC-время, а локализация выполняется на уровне приложения.

Причины:

  • предотвращение конфликтов при смене часовых поясов
  • корректная сортировка событий
  • единое представление времени в распределённых системах

Рекомендованная модель хранения

В большинстве архитектур используется следующая схема:

  • база данных хранит UTC timestamp
  • API передаёт ISO 8601 или timestamp
  • фронтенд форматирует через Day.js

Пример хранения:

created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP

или:

created_at BIGINT NOT NULL

Преобразование данных в Day.js

При получении данных из базы используется унифицированный парсинг:

const createdAt = dayjs(dbRow.created_at);

Day.js автоматически интерпретирует:

  • ISO строки
  • timestamp (ms)
  • Date объекты

Дополнительная нормализация:

const normalized = dayjs(dbRow.created_at).utc();

Потеря точности и проблемы сериализации

Разные базы данных и драйверы могут по-разному трактовать точность:

  • PostgreSQL TIMESTAMP — микросекунды
  • MySQL DATETIME — секунды или миллисекунды (в зависимости от версии)
  • JavaScript Date — миллисекунды

Day.js работает на уровне миллисекунд, поэтому возможны потери точности при миграции.

Пример проблемы:

dayjs('2026-05-23T14:30:00.123456Z')

Микросекунды будут отброшены.


Сравнение стратегий хранения

1. TIMESTAMP в UTC

  • оптимален для SQL-систем
  • поддерживает индексацию
  • используется в большинстве production-систем

2. BIGINT (Unix time)

  • максимальная производительность
  • минимальная абстракция
  • требует строгого контроля единиц измерения

3. ISO 8601 строка

  • удобна для API
  • хуже индексируется
  • требует парсинга при каждом запросе

Day.js в слое сериализации

Day.js часто используется как слой между базой и бизнес-логикой:

function mapUser(row) {
  return {
    id: row.id,
    createdAt: dayjs(row.created_at).utc(),
    updatedAt: dayjs(row.updated_at).utc()
  };
}

Форматирование для клиента:

function toResponse(entity) {
  return {
    ...entity,
    createdAt: entity.createdAt.format(),
    updatedAt: entity.updatedAt.format()
  };
}

Поведение при миграциях данных

При переносе данных между системами критично соблюдать:

  • единый стандарт UTC
  • единый тип хранения (timestamp или ISO)
  • отсутствие локальных преобразований на уровне базы

Типичная ошибка миграции:

  • сохранение локального времени без временной зоны
  • повторное применение timezone-логики на новом сервере
  • несогласованность DST (летнее время)

Day.js с UTC-плагином устраняет часть этих проблем:

const safe = dayjs(date).utc().valueOf();

Индексация и производительность

С точки зрения СУБД:

  • числовые timestamp индексируются быстрее строк
  • операции сравнения выполняются дешевле
  • диапазонные запросы оптимальны

Пример SQL-запроса:

SEL ECT *
FR OM events
WH ERE created_at BETWEEN 1716400000000 AND 1716500000000;

Day.js помогает формировать такие диапазоны:

const start = dayjs().startOf('day').valueOf();
const end = dayjs().endOf('day').valueOf();

Нормализация входящих данных

Любые внешние данные требуют нормализации перед записью:

const normalizedDate = dayjs(inputDate).utc().toISOString();

или для timestamp:

const normalizedTs = dayjs(inputDate).valueOf();

Это устраняет:

  • неоднозначность локального времени
  • ошибки форматов
  • различия клиентских настроек

Практика безопасного хранения

На уровне архитектуры применяются следующие принципы:

  • хранение только UTC
  • отсутствие бизнес-логики в базе относительно времени
  • единый формат для всех сервисов
  • использование Day.js как единой точки преобразования

Эта модель обеспечивает предсказуемость поведения при масштабировании, распределённых вычислениях и работе с историческими данными.