Unix-метки времени: конвертация и типичные подводные камни
Unix-метка времени — одна из самых простых и в то же время наиболее неправильно понимаемых концепций в программировании. Это количество секунд, прошедших с 1 января 1970 года, 00:00:00 UTC — момента, известного как эпоха Unix. Несмотря на простоту, метки времени являются источником багов, связанных с часовыми поясами, точностью и переполнением.
Что такое эпоха Unix?
Эпоха Unix — 1 января 1970 года, 00:00:00 UTC — была выбрана как начальная точка для Unix-времени. Каждая метка времени измеряется относительно этого момента:
| Метка времени | Дата и время (UTC) |
|---|---|
| 0 | 1 января 1970 00:00:00 |
| 86400 | 2 января 1970 00:00:00 |
| 1000000000 | 9 сентября 2001 01:46:40 |
| 1700000000 | 14 ноября 2023 22:13:20 |
| 2000000000 | 18 мая 2033 03:33:20 |
Отрицательные метки времени представляют даты до эпохи. Например, -86400 — это 31 декабря 1969 года.
Конвертируйте метки времени мгновенно с помощью нашего Конвертера меток времени.
Секунды и миллисекунды
Это самый распространённый источник путаницы. Разные системы используют разную точность:
| Система | Точность | Пример |
|---|---|---|
| Unix/POSIX | Секунды | 1700000000 |
| JavaScript | Миллисекунды | 1700000000000 |
| Java (System.currentTimeMillis) | Миллисекунды | 1700000000000 |
| Python (time.time) | Секунды (дробные) | 1700000000.123 |
| PostgreSQL (extract epoch) | Секунды (дробные) | 1700000000.123456 |
Правило большого пальца: если число содержит 13 цифр — это миллисекунды. Если 10 цифр — это секунды.
// JavaScript returns milliseconds
const nowMs = Date.now(); // 1700000000000
const nowSec = Math.floor(nowMs / 1000); // 1700000000
Обработка часовых поясов
Unix-метки времени всегда в UTC. Они не содержат информации о часовом поясе. На самом деле это преимущество — обеспечивается универсальная точка отсчёта.
Путаница возникает при преобразовании меток времени в человекочитаемые даты:
const ts = 1700000000;
const date = new Date(ts * 1000);
date.toUTCString(); // "Tue, 14 Nov 2023 22:13:20 GMT"
date.toLocaleString(); // Depends on user's local time zone
date.toISOString(); // "2023-11-14T22:13:20.000Z"
Лучшая практика: храните и передавайте метки времени в UTC. Конвертируйте в местное время только на уровне отображения, максимально близко к пользователю.
Проблема 2038 года
Традиционные Unix-системы хранят метки времени как 32-битное знаковое целое число. Максимальное значение — 2 147 483 647, что соответствует 19 января 2038 года, 03:14:07 UTC.
После этого момента 32-битные метки времени переполняются до отрицательных значений, откатываясь к 13 декабря 1901 года. Это аналогично ошибке Y2K.
Текущее состояние:
- Большинство современных систем используют 64-битные метки времени (хватит до 292 миллиардов лет)
- Ядро Linux полностью перешло на 64-битные метки времени с версии 5.6 (2020)
- Встраиваемые системы и устаревшие базы данных остаются в зоне риска
- Если вы разрабатываете ПО, работающее с датами после 2038 года, проверьте хранение меток времени
Конвертация на разных языках
JavaScript
// Current timestamp (seconds)
const now = Math.floor(Date.now() / 1000);
// Timestamp to Date
const date = new Date(1700000000 * 1000);
// Date to timestamp
const ts = Math.floor(new Date('2023-11-14').getTime() / 1000);
Python
import time, datetime
# Current timestamp
now = int(time.time())
# Timestamp to datetime
dt = datetime.datetime.fromtimestamp(1700000000, tz=datetime.timezone.utc)
# Datetime to timestamp
ts = int(dt.timestamp())
SQL (PostgreSQL)
-- Current timestamp
SELECT EXTRACT(EPOCH FROM NOW());
-- Timestamp to date
SELECT TO_TIMESTAMP(1700000000);
-- Date to timestamp
SELECT EXTRACT(EPOCH FROM '2023-11-14'::timestamp);
Типичные подводные камни
1. Смешивание секунд и миллисекунд
Если дата отображается как январь 1970 года, вы, вероятно, передали секунды там, где ожидались миллисекунды (или наоборот). Всегда проверяйте, какую точность ожидает API.
2. Игнорирование часовых поясов в строках дат
Парсинг "2023-11-14" без часового пояса создаёт дату в местном часовом поясе, который зависит от расположения сервера. Всегда указывайте часовой пояс: "2023-11-14T00:00:00Z".
3. Точность чисел с плавающей запятой
При хранении меток времени в виде чисел с плавающей запятой можно потерять точность выше миллисекунд. Для микро- или наносекундной точности используйте целые числа с соответствующим множителем.
4. Секунды координации
Unix-метки времени не учитывают секунды координации. Unix-день всегда составляет ровно 86 400 секунд, хотя реальные дни UTC иногда содержат 86 401 секунду. Для большинства приложений это несущественно. Для научных или спутниковых приложений используйте TAI (Международное атомное время).
ISO 8601: человекочитаемая альтернатива
Хотя метки времени отлично подходят для вычислений, ISO 8601 — стандарт для человекочитаемого представления дат:
2023-11-14T22:13:20Z # UTC
2023-11-14T17:13:20-05:00 # Eastern Time
2023-11-14 # Date only
Большинство API должны принимать и возвращать строки в формате ISO 8601. Используйте метки времени внутри системы для вычислений и хранения.
FAQ
Почему Unix-время начинается с 1 января 1970 года?
Дата была выбрана произвольно, когда Unix разрабатывался в Bell Labs в начале 1970-х. Нужна была достаточно недавняя дата, чтобы не тратить биты на далёкое прошлое. Поскольку 32-битные целые числа могут хранить около 68 лет в каждом направлении, начало с 1970 года покрывало даты с 1901 по 2038 год.
Хранить даты как метки времени или форматированные строки в базе данных?
Храните даты как метки времени (целые числа или нативные типы даты) для эффективной сортировки, сравнения и арифметики. Форматированные строки сложнее правильно сортировать и запрашивать. Большинство баз данных имеют нативные типы для работы с датами. Оставьте строковое форматирование для отображения и ответов API.
Связанные ресурсы
- Конвертер меток времени — Конвертация между Unix-метками времени и человекочитаемыми датами
- Лучшие практики форматирования JSON — Работа с датами в JSON-ответах
- Руководство по UUID — Ещё один распространённый формат идентификаторов с вариантами на основе времени