Баги с временными метками: Секунды против миллисекунд
Вы отлаживаете интеграцию API, и даты отображаются как 1970 или 2053 год. Или, возможно, ваши временные метки работают отлично при локальной разработке, но ломаются в продакшене. Это классические симптомы путаницы с форматами временных меток: смешивание секунд и миллисекунд, неправильная обработка часовых поясов или использование неверного формата Unix epoch. Понимание того, как временные метки работают в разных системах, критически важно для надёжной обработки дат в API, базах данных и логах.
Проблема: Неопределённость Unix timestamp
Unix timestamp представляет время как количество единиц, прошедших с 1 января 1970 года 00:00:00 UTC (эпоха Unix). Но есть критическая неопределённость: какие единицы?
- Секунды:
1704985200(10 цифр, стандартный Unix timestamp) - Миллисекунды:
1704985200000(13 цифр, стандарт JavaScript) - Микросекунды:
1704985200000000(16 цифр, используется в некоторых БД) - Наносекунды:
1704985200000000000(19 цифр, высокоточные системы)
Проблема? Нет универсального стандарта. Разные языки, API и системы используют разные единицы, а временные метки не несут метаданных о своём формате.
Классический баг: Дата отображается как 1970 или 2053
Вы получаете временную метку от API и конвертируете её в дату. Результат?
// API возвращает timestamp в секундах
const apiTimestamp = 1704985200;
// JavaScript ожидает миллисекунды
const date = new Date(apiTimestamp);
console.log(date.toISOString());
// Вывод: 1970-01-20T18:03:05.200Z ❌ Неправильно!Дата интерпретируется как 20 января 1970 года вместо 11 января 2026. Почему? Конструктор Date() в JavaScript ожидает миллисекунды, но API отправил секунды.
Решение: Конвертировать секунды в миллисекунды
// Умножить на 1000 для конвертации секунд в миллисекунды
const date = new Date(apiTimestamp * 1000);
console.log(date.toISOString());
// Вывод: 2026-01-11T10:00:00.000Z ✅ Правильно!И наоборот, если вы отправляете временные метки в API, который ожидает секунды, но вы предоставляете миллисекунды, даты будут отображаться в далёком будущем (год 2053+).
Как определить формат временной метки
При работе с неизвестными форматами временных меток используйте количество цифр как эвристику для определения:
function detectTimestampUnit(timestamp: number): string {
const digits = timestamp.toString().length;
if (digits === 10) return 'seconds';
if (digits === 13) return 'milliseconds';
if (digits === 16) return 'microseconds';
if (digits === 19) return 'nanoseconds';
return 'unknown';
}
// Примеры
console.log(detectTimestampUnit(1704985200)); // 'seconds'
console.log(detectTimestampUnit(1704985200000)); // 'milliseconds'Эта эвристика работает до примерно 2286 года (когда Unix timestamp в секундах достигнет 10 миллиардов и станет 11-значным). Для практических целей она надёжна на десятилетия вперёд.
Форматы временных меток в разных языках программирования
Разные языки программирования используют разные соглашения:
JavaScript / TypeScript
Date.now()возвращает миллисекундыnew Date(timestamp)ожидает миллисекундыdate.getTime()возвращает миллисекунды
const now = Date.now(); // 1704985200000 (миллисекунды)
const date = new Date(now);
console.log(date.toISOString()); // '2026-01-11T10:00:00.000Z'Python
time.time()возвращает секунды (с десятичной дробью для точности меньше секунды)datetime.timestamp()возвращает секунды
import time
from datetime import datetime
now = time.time() # 1704985200.123456 (секунды)
date = datetime.fromtimestamp(now)
print(date.isoformat()) # '2026-01-11T10:00:00.123456'Java
System.currentTimeMillis()возвращает миллисекундыInstant.now().toEpochMilli()возвращает миллисекундыInstant.now().getEpochSecond()возвращает секунды
long millis = System.currentTimeMillis(); // 1704985200000
long seconds = millis / 1000; // 1704985200Go
time.Now().Unix()возвращает секундыtime.Now().UnixMilli()возвращает миллисекундыtime.Now().UnixNano()возвращает наносекунды
package main
import (
"fmt"
"time"
)
func main() {
now := time.Now()
fmt.Println(now.Unix()) // 1704985200 (секунды)
fmt.Println(now.UnixMilli()) // 1704985200000 (миллисекунды)
}Хранение временных меток в базах данных
Базы данных также используют различные соглашения:
- PostgreSQL:
TIMESTAMPхранит значения datetime, а не числовые временные метки. ИспользуйтеEXTRACT(EPOCH FROM timestamp)для получения Unix-секунд. - MySQL:
TIMESTAMPхранит значения datetime. ИспользуйтеUNIX_TIMESTAMP()для получения секунд. - MongoDB: Тип
Dateхранит миллисекунды с начала эпохи Unix (совместимо с JavaScript). - Redis: Хранит временные метки как строки или числа. Обычная практика — использовать секунды для TTL, миллисекунды для временных меток событий.
Пример PostgreSQL
-- Конвертировать timestamp в Unix-секунды
SELECT EXTRACT(EPOCH FROM NOW()); -- 1704985200.123456
-- Конвертировать Unix-секунды в timestamp
SELECT TO_TIMESTAMP(1704985200); -- '2026-01-11 10:00:00+00'Ловушки с часовыми поясами
Unix timestamp всегда в UTC, но для их отображения требуется контекст часового пояса. Типичные ошибки:
Ошибка #1: Отображение UTC как местного времени
const timestamp = 1704985200; // 2026-01-11 10:00:00 UTC
const date = new Date(timestamp * 1000);
// ❌ Неправильно: Отображается в локальном часовом поясе браузера
console.log(date.toString());
// В Нью-Йорке: 'Sat Jan 11 2026 05:00:00 GMT-0500'
// В Токио: 'Sat Jan 11 2026 19:00:00 GMT+0900'
// ✅ Правильно: Явно отобразить в UTC
console.log(date.toISOString());
// '2026-01-11T10:00:00.000Z' (всегда UTC)Ошибка #2: Создание временных меток из местного времени
// ❌ Неправильно: Предполагает локальный часовой пояс
const date = new Date('2026-01-11 10:00:00');
console.log(date.getTime() / 1000);
// Результат варьируется в зависимости от часового пояса пользователя!
// ✅ Правильно: Явно указать UTC
const utcDate = new Date('2026-01-11T10:00:00Z');
console.log(utcDate.getTime() / 1000); // 1704985200 (последовательно)Решение: Всегда использовать ISO 8601 с часовым поясом
При работе с читаемыми человеком датами всегда используйте формат ISO 8601 с явным указанием часового пояса:
- UTC:
2026-01-11T10:00:00Zили2026-01-11T10:00:00+00:00 - Нью-Йорк (EST):
2026-01-11T05:00:00-05:00 - Токио (JST):
2026-01-11T19:00:00+09:00
Реальный сценарий отладки
Вы интегрируетесь с внешним API, и временные метки выглядят неправильно. Вот как отладить:
Шаг 1: Проверить сырую временную метку
const apiResponse = {
"created_at": 1704985200,
"updated_at": 1704985200000
};
console.log('created_at digits:', apiResponse.created_at.toString().length);
// Вывод: 10 (вероятно, секунды)
console.log('updated_at digits:', apiResponse.updated_at.toString().length);
// Вывод: 13 (вероятно, миллисекунды)Шаг 2: Протестировать конвертацию с известной датой
Конвертируйте временную метку и проверьте, соответствует ли она известной дате. Например, если в документации API сказано, что created_at — это 11 января 2026 года 10:00:00 UTC:
// Тестировать как секунды
const dateSeconds = new Date(1704985200 * 1000);
console.log(dateSeconds.toISOString());
// '2026-01-11T10:00:00.000Z' ✅ Совпадает!
// Тестировать как миллисекунды (неправильно)
const dateMillis = new Date(1704985200);
console.log(dateMillis.toISOString());
// '1970-01-20T18:03:05.200Z' ❌ Неправильный годШаг 3: Проверить обработку часового пояса
// Проверить, соответствует ли отображаемое время ожиданиям
const date = new Date(1704985200 * 1000);
console.log('ISO (UTC):', date.toISOString());
// '2026-01-11T10:00:00.000Z'
console.log('Local string:', date.toString());
// 'Sat Jan 11 2026 13:00:00 GMT+0300 (Москва, стандартное время)'
console.log('UTC string:', date.toUTCString());
// 'Sat, 11 Jan 2026 10:00:00 GMT'Типичные ошибки конвертации временных меток
Ошибка: Дата на 50 лет в прошлом или будущем
Причина: Смешивание секунд и миллисекунд
Решение: Умножить на 1000, если временная метка в секундах, разделить на 1000, если в миллисекундах
Ошибка: Дата сдвинута на несколько часов
Причина: Путаница с часовыми поясами (отображение UTC как местного или наоборот)
Решение: Всегда использовать toISOString() или toUTCString() для последовательного отображения UTC
Ошибка: Временные метки работают локально, но ломаются в продакшене
Причина: Локальная машина разработки имеет другой часовой пояс, чем продакшен-серверы
Решение: Установить часовой пояс сервера в UTC, всегда использовать UTC для внутренних временных меток, конвертировать в местные часовые пояса только для отображения
Ошибка: Invalid Date при парсинге временных меток
Причина: Формат временной метки не распознан new Date()
Решение: Использовать числовые временные метки или строки ISO 8601, избегать форматов дат, зависящих от локали
Лучшие практики обработки временных меток
- Хранить временные метки как Unix-секунды или миллисекунды в UTC — никогда не хранить местное время без часового пояса
- Использовать формат ISO 8601 для читаемых человеком дат — всегда включать часовой пояс (
Zили±HH:MM) - Документировать единицы временных меток в API-контрактах — указывать секунды, миллисекунды или формат ISO
- Определять формат по количеству цифр — 10 цифр = секунды, 13 = миллисекунды
- Использовать библиотеки для работы с датами при сложных операциях — например, date-fns, Luxon, Day.js (вместо сырого
Date) - Тестировать граничные случаи — границы года, високосные секунды, переходы на летнее время
- Установить часовой пояс сервера в UTC — избежать дрейфа часового пояса между окружениями
Использование инструментов конвертации временных меток
При отладке проблем с временными метками используйте конвертер временных меток для:
- Конвертации между секундами, миллисекундами и читаемыми человеком датами
- Тестирования значений временных меток с известными датами
- Проверки обработки часовых поясов
- Генерации временных меток для тестирования
- Проверки текущего Unix времени для справки
Конвертер дат в DevToys Pro автоматически определяет формат временной метки, обрабатывает множество часовых поясов и обеспечивает мгновенную конвертацию между Unix timestamp, ISO 8601 и читаемыми человеком форматами.
Чек-лист валидации
Перед развёртыванием кода обработки временных меток проверьте:
- ✅ Временные метки конвертированы в правильные единицы (секунды ↔ миллисекунды)
- ✅ UTC используется для хранения и API-коммуникации
- ✅ Часовой пояс явно указан при отображении дат
- ✅ Протестированы граничные случаи (границы года, високосные годы)
- ✅ В документации API указан формат временных меток
- ✅ Часовой пояс сервера установлен в UTC
- ✅ Используются библиотеки для работы с датами при сложной логике часовых поясов
Ключевые выводы
- Unix timestamp может быть в секундах (10 цифр) или миллисекундах (13 цифр)
- JavaScript ожидает миллисекунды; многие API отправляют секунды — конвертируйте соответственно
- Всегда храните и передавайте временные метки в UTC, конвертируйте в местное время только для отображения
- Используйте формат ISO 8601 с явным часовым поясом для читаемых человеком дат
- Определяйте формат временной метки по количеству цифр как эвристику
- Тестируйте конвертацию временных меток с известными датами для проверки корректности
- Документируйте единицы временных меток в API-контрактах для предотвращения багов интеграции
Связанные инструменты:
- Конвертер Unix Timestamp — Конвертация между временными метками, датами и часовыми поясами
- Форматтер JSON — Форматирование API-ответов с временными метками
- Тестер JSONPath — Извлечение полей временных меток из API-ответов