Временная метка Unix выглядит как число. В этом ее прелесть, а также источник многих ошибок.
Вы можете сохранить его в базе данных, отправить по сети, сравнить с другой временной меткой или использовать для вычисления времени, затраченного на что-либо. Вам не нужно согласовывать формат даты или часовой пояс только для сравнения двух обычных временных меток.
Но есть загвоздка: временная метка представляет время в соответствии с соглашением. Это не гарантирует, что часы работают правильно, что два события получают разные значения или что число надежно измеряет прошедшее время.
В этом подробном руководстве мы объясним, что такое временная метка Unix и ее использование на практических примерах.
1. Что такое временная метка Unix?
Эпоха Unix — это точка отсчета: 1970-01-01 00:00:00 UTC
Эпоха Unix: 1970-01-01 00:00:00 UTC
| Временная метка Unix | Значение | Дата и время в формате UTC |
|---|---|---|
0 | Сама эпоха | 1970-01-01 00:00:00 UTC |
1 | Одна секунда после эпохи | 1970-01-01 00:00:01 UTC |
-1 | Одна секунда до эпохи | 1969-12-31 23:59:59 UTC |
Временная метка Unix в общепринятой форме, основанной на секундах, представляет собой количество секунд, отсчитываемых от этой точки отсчета в соответствии с соглашением о времени Unix/POSIX. Отрицательное значение соответствует времени до начала эпохи в системах, которые его поддерживают.
Open Group описывает это как значение, приблизительно равное количеству секунд, прошедших с начала эпохи, — важное слово, к которому мы вернемся позже.
Например:
| временная метка Unix | дата и время в формате UTC |
|---|---|
0 | 1970-01-01 00:00:00 |
1 | 1970-01-01 00:00:01 |
60 | 1970-01-01 00:01:00 |
86400 | 1970-01-02 00:00:00 |
-1 | 1969-12-31 23:59:59 |
1483228800 | 2017-01-01 00:00:00 |
Обратите внимание, что 86400 обозначает следующий календарный день. В обычной модели времени Unix в сутках 86 400 секунд. Високосные секунды — это когда простая модель перестает идеально соответствовать всемирному координированному времени.
Пожалуйста, помните, что время Unix появилось вместе с Unix, но для его использования не обязательно запускать Unix. Linux, macOS, Windows API, браузеры, базы данных и распределенные системы — все они имеют свои способы представления времени, многие из которых используют одну и ту же эпоху.
2. Секунды, миллисекунды, микросекунды и наносекунды
Первое, что нужно знать при чтении временной метки, — это ее единица измерения.
Временная метка Unix сама по себе не указывает, что означает это число: секунды, миллисекунды или что-то меньшее. Единица измерения определяется окружающим API или форматом данных.
| Единица измерения | Одна единица равна | Типичное использование |
|---|---|---|
| Секунды | 1 секунда | Временные метки Unix, API, инструменты оболочки |
| Миллисекунды (мс) | 0,001 секунды | JavaScript, многие API приложений |
| Микросекунды (µs) | 0,000001 секунды | Временные метки базы данных, данные о производительности |
| Наносекунды (ns) | 0,000000001 секунды | Системное программирование, синхронизация с высоким разрешением |
На момент написания этого руководства временная метка для даты в 2026 году имела примерно такие значения, в зависимости от единицы измерения:
| Единица измерения | Приблизительное количество |
|---|---|
| Секунды | 1,798,000,000 |
| Миллисекунды | 1,798,000,000,000 |
| Микросекунды | 1,798,000,000,000,000 |
| Наносекунды | 1,798,000,000,000,000,000 |
Это иллюстративные значения, а не конкретные моменты времени.
Обратите внимание, что при изменении единицы измерения числовое значение существенно меняется, но не меняется сам момент времени.
Классическая ошибка: секунды и миллисекунды
Предположим, API возвращает следующее:
{
"created_at": 1483228800
}
Это означает 2017-01-01 00:00:00 UTC , если API определяет поле как Unix-секунды.
Теперь рассмотрим JavaScript:
const timestamp = 1483228800; console.log(new Date(timestamp).toISOString());
Вы получите дату января 1970 года, а не 2017 года.
Почему? Конструктор Date в JavaScript ожидает миллисекунды, а не секунды Unix. Такое поведение описано в MDN.
Сначала преобразуйте единицу измерения:
const timestampSeconds = 1483228800; const date = new Date(timestampSeconds * 1000); console.log(date.toISOString()); // 2017-01-01T00:00:00.000Z
Чтобы получить текущую временную метку Unix в секундах:
const nowSeconds = Math.floor(Date.now() / 1000);
Чтобы получить значение в миллисекундах:
const nowMilliseconds = Date.now();
Оба варианта допустимы. Ошибка заключается в использовании одного варианта там, где ожидается другой.
Важно: Никогда не угадывайте единицу измерения временной метки по количеству цифр. Укажите единицу измерения в контракте API, схеме базы данных и именах переменных.
Отдавайте предпочтение таким названиям, как created_at_ms, epoch_seconds, или timestamp_ns вместо расплывчатых, таких как time.
3. Преобразование временных меток Unix из командной строки
Для проверки временных меток не нужен язык программирования. Командная строка Unix всегда была хорошим способом узнать, как работает время.
В следующем наборе примеров используется GNU date, который обычно устанавливается в Linux. GNU описывает эти преобразования в руководстве по Coreutils.
Получение текущей временной метки Unix
date +%s
Пример вывода:
1792589190
Результат меняется каждую секунду. Фактическое значение будет зависеть от того, когда вы запустите команду.
Преобразование временной метки в удобочитаемую дату
date -u -d '@1483228800' '+%Y-%m-%dT%H:%M:%SZ'
Пример вывода:
2017-01-01T00:00:00Z
Вот что означают эти параметры:
-uуказываетdateна использование UTC.-dсообщает GNUdateкакую дату или временную метку нужно проанализировать.@1483228800означает временную метку в стиле Unix.+%Y-%m-%dT%H:%M:%SZуказывает формат вывода.
@ перед числом имеет значение для GNU date: оно указывает команде, что это число следует интерпретировать как временную метку, а не как обычную строку с датой.
Преобразование даты в временную метку
Выполнить:
date -u -d '2017-01-01 00:00:00' +%s
Пример вывода:
1483228800
Вы можете использовать эту команду в скриптах, при анализе журналов и отладке.
Отображать доли секунды
GNU date также поддерживает форматирование наносекунд:
date +'%Y-%m-%dT%H:%M:%S.%N%:z'
Пример вывода:
2026-10-21T18:59:21.575616862+05:30
Этот вывод иллюстрирует форматирование наносекунд. Это не означает, что часы показывают время с точностью до наносекунды. Реальное разрешение и точность часов зависят от системы.
Чтобы быстро узнать о заявленном разрешении:
date --resolution
Эти параметры являются расширениями GNU. В macOS и некоторых системах BSD используется другой date синтаксис. Например, в macOS временную метку можно преобразовать с помощью следующей команды:
date -u -r 1483228800 '+%Y-%m-%dT%H:%M:%SZ'
Если команда по-разному работает на разных компьютерах, прежде чем винить временную метку, проверьте, какую реализацию вы используете.
4. Часовые пояса не влияют на временную метку
Это одна из самых полезных вещей, которые нужно знать.
Временная метка Unix не содержит информации о часовом поясе. Она представляет собой момент времени в соответствии с соглашением о времени Unix.
Один и тот же момент времени может иметь разные представления в удобочитаемом формате.
Например, момент времени, представленный 1483228800:
| Часовой пояс | Отображаемое время |
|---|---|
| UTC | 2017-01-01 00:00:00 |
Europe/Moscow | 2017-01-01 03:00:00 |
| Нью-Йорк | 2016-12-31 19:00:00 |
Время одно и то же. Меняется только его представление.
Вы можете проверить пример с Москвой в Linux:
TZ=Europe/Moscow date -d '@1483228800'
Результат:
Sun Jan 1 03:00:00 MSK 2017
Затем сравните с UTC:
TZ=UTC date -d '@1483228800'
Результат:
Sun Jan 1 00:00:00 UTC 2017
Почему это важно
Представьте, что вы расследуете инцидент, и в журналах вашего сервера написано:
Получен запрос: 1790000000
Коллега из другой страны спрашивает, в какое время это произошло.
Если вы преобразуете число, используя местный часовой пояс вашего компьютера, то время, которое увидит коллега, может отличаться от ожидаемого. Часы могут показывать разное время, но это не значит, что они показывают разные моменты времени; просто вы использовали разные часовые пояса.
Для журналов, API и распределенных систем обычно проще всего использовать универсальное координированное время. Для вывода информации, предназначенной для людей, преобразуйте время в соответствующий часовой пояс пользователя.
Примечание: часовой пояс — это не просто фиксированное смещение от UTC. Во многих странах часы переводят в соответствии с правилами перехода на летнее время, и правительства могут менять эти правила. Такие библиотеки, как zoneinfo в Python, используют информацию из базы данных часовых поясов для учета этих изменений.
Поэтому, когда вам нужно запланировать что-то в определенном месте, название вроде America/New_York часто оказывается полезнее, чем просто -05:00.
5. UTC, Unix-время, TAI и дополнительные секунды
Теперь мы подошли к самому интересному.
Люди часто говорят: «Unix-время — это UTC». Это удобное сокращение, но оно скрывает важное различие.
UTC — это шкала времени. Unix-время — это способ представления времени с использованием системы отсчета, основанной на эпохе. Эти системы тесно связаны, но ведут себя по-разному в случае високосных секунд.
Давайте разберемся в основных понятиях.
| Термин | Что он означает |
|---|---|
| UTC | Международная шкала времени, используемая для гражданского времени |
| Эпоха Unix | Контрольная точка в 1970-01-01 00:00:00 UTC |
| Время Unix / POSIX | Числовое представление на основе эпохи, которое обычно игнорирует високосные секунды |
| TAI | Международное атомное время, непрерывная шкала атомного времени |
| UT1 | Шкала времени, основанная на вращении Земли |
UTC поддерживается близким ко времени вращения Земли, UT1, путем применения високосных секунд по традиционным правилам. TAI, напротив, непрерывно продвигается вперед без корректировки UTC на високосную секунду. Международное бюро мер и весов (МБМВ) объясняет эту взаимосвязь в своих решениях по всемирному координированному времени.
Почему бы просто не перевести все часы на атомное время?
Потому что людей волнует вращение Земли. Полдень традиционно означал время, близкое к тому, когда Солнце находится в самой высокой точке на небе. Атомные часы гораздо более стабильны, чем вращение Земли, поэтому в UTC исторически вносились периодические корректировки на одну секунду, чтобы синхронизировать эти два показателя.
Именно здесь на сцену выходят високосные секунды.
Високосная секунда 2016 года
31 декабря 2016 года в UTC была добавлена следующая последовательность:
23:59:58 23:59:59 23:59:60 <- дополнительная секунда 00:00:00 00:00:01
В приведенном выше выводе средний 23:59:60 — настоящий. Это не опечатка. RFC 3339, стандарт представления даты и времени в Интернете, явно допускает использование второй 60 секунды, если это позволяют правила високосных секунд.
Но обычное представление времени в Unix не позволяет получить переносимую и уникальную целочисленную временную метку для этой добавленной секунды.
В типичных системах POSIX значения на границе выглядят так:
| показания часов в формате UTC | обычная временная метка Unix |
|---|---|
| 2016-12-31 23:59:58 | 1483228798 |
| 2016-12-31 23:59:59 | 1483228799 |
| 2016-12-31 23:59:60 | Отдельная переносимая временная метка Unix |
| 2017-01-01 00:00:00 | 1483228800 |
| 2017-01-01 00:00:01 | 1483228801 |
Это ключевой момент: обычное сопоставление Unix не может однозначно отобразить показания часов в формате UTC во время високосной секунды.
Оговорка заключается в том, что точное поведение зависит от реализации часов и обработки их високосной секунды. Машина может повторить секунду, перевести свои часы или размазать високосную секунду по более длительному интервалу. В Linux, CLOCK_REALTIME например, обычно игнорируются високосные секунды и могут быть скорректированы NTP с точностью до високосной секунды.
Таким образом, временные метки Unix уникальны в 99,9999% случаев. Мы бы сказали:
Временные метки Unix представляют собой моменты времени в соответствии с принятым соглашением, но они не являются уникальными идентификаторами событий, а дополнительные секунды указывают на несоответствие между этим соглашением и всемирным координированным временем.
Это утверждение остается верным даже после того, как дополнительные секунды перестанут вводиться.
Важное событие: всемирное координированное время меняется
По состоянию на 21 октября 2026 года это уже не просто предложение на будущее.
В 2026 году Генеральная конференция по мерам и весам приняла резолюцию 5, в которой говорится, что непрерывное всемирное координированное время вступит в силу 20 мая 2027 года. В резолюции также установлена максимально допустимая разница в один час между всемирным координированным временем и всемирным временем 1. Это решение направлено на то, чтобы отказаться от корректировки времени на одну секунду и избежать рисков для критически важной цифровой инфраструктуры.
Почему было принято такое решение?
В резолюции 2022 года уже признавалось, что високосные секунды могут приводить к серьезным сбоям в цифровой инфраструктуре. Первая отрицательная високосная секунда, когда секунда не добавляется, а убирается, так и не была реализована, и соответствующие программные системы не были к ней готовы.
Решение 2026 года приближает этот переход. Новые правила не меняют эпоху Unix и не делают так, чтобы исторические временные метки Unix учитывали високосные секунды. Они меняют способ синхронизации всемирного координированного времени с вращением Земли в будущем.
Для обычных разработчиков разумным решением по-прежнему будет использование стандартных API для работы с временными метками, синхронизация часов и отказ от предположения, что настенные часы — это идеальный счетчик прошедших секунд.
6. Точность — это не аккуратность
Это еще одна ловушка.
Предположим, вы записываете текущее время следующим образом:
1790000000.123456789
У вас девять цифр после запятой. Это позволяет получить представление о времени с точностью до наносекунды.
Но само по себе это ничего не говорит о точности часов.
Рассмотрим три отдельных понятия:
| Концепция | Вопрос, на который он отвечает |
|---|---|
| Точность | С какой точностью может быть представлено значение? |
| Разрешение | Какую наименьшую разницу могут показать часы? |
| Точность | Насколько близко указанное время к предполагаемому эталонному времени? |
У вас могут быть часы, которые показывают время с точностью до наносекунды, но отстают от UTC на 200 миллисекунд. У них высокая детализация, но низкая абсолютная точность.
Аналогичным образом два вызова API часов могут возвращать одно и то же значение, поскольку разрешение часов ограничено, даже если вызовы были выполнены в разное время. Python явно указывает, что последовательные вызовы datetime.now() могут возвращать одно и то же значение в зависимости от точности базовых часов.
Это различие важно для баз данных, профилирования, телеметрии и распределенных систем.
Поле с девятью знаками после запятой не является доказательством того, что девять знаков после запятой несут значимую информацию.
7. Время по настенным часам и монотонное время
Это различие спасло многие производственные системы от незаметных на первый взгляд ошибок.
Временная метка Unix обычно берется из настенных часов: часов, предназначенных для отображения текущей даты и времени.
В Linux CLOCK_REALTIME используется именно такое представление времени. Но системные часы можно настроить. Администратор может изменить их, а синхронизация по протоколу NTP может изменить их скорость, а также сдвинуть их вперед или назад.
Представьте, что вы измеряете время выполнения такого запроса:
Время начала: 10:00:05 Время окончания: 10:00:03
Запрос занял меньше двух секунд?
Конечно, нет. Часы сдвинулись назад между двумя вашими измерениями.
Вот почему в операционных системах используются монотонные часы.
Монотонные часы показывают значение, которое не меняется в обратном направлении. Их начало не совпадает с эпохой Unix; это точка отсчета, определяемая реализацией, которая в Linux часто связана со временем работы системы.
Поэтому они не подходят для обычной календарной отметки времени, но отлично подходят для измерения продолжительности.
| Задача | Подходящий тип часов |
|---|---|
| Запись времени входа пользователя | Настенные часы / временная метка Unix |
| Запись времени создания строки в базе данных | Настенные часы / временная метка базы данных |
| Измерение времени выполнения функции | Монотонные часы |
| Реализация таймаута | Монотонные часы |
| Запланировать отчет на определенную дату | Время по настенным часам с учетом календаря и часового пояса |
В Linux на CLOCK_MONOTONIC не влияют скачки времени по настенным часам, хотя его скорость все равно можно регулировать. Другие варианты часов имеют другую семантику; например, CLOCK_BOOTTIME также учитывает время, проведенное в режиме ожидания системы.
Подробнее можно прочитать на странице руководства clock_getres(2).
Практическое правило
Не измеряйте прошедшее время, вычитая две обычные временные метки Unix, если можно использовать монотонные часы.
Временная метка показывает, когда, по мнению системы, произошло то или иное событие. Монотонные часы помогают определить, сколько времени прошло между двумя операциями.
Это разные задачи.
8. Практические примеры на Python, JavaScript и SQL
Принципы становятся понятнее, когда вы применяете их в коде.
Python
Преобразование временной метки в дату и время в формате UTC:
from datetime import datetime, timezone timestamp = 1483228800 dt = datetime.fromtimestamp(timestamp, tz=timezone.utc) print(dt.isoformat())
Пример вывода:
2017-01-01T00:00:00+00:00
Преобразуем дату и время с учетом часового пояса обратно в метку времени:
from datetime import datetime, timezone dt = datetime(2017, 1, 1, 0, 0, 0, tzinfo=timezone.utc) print(int(dt.timestamp()))
Пример вывода:
1483228800
Обратите внимание на timezone.utc. Наивная дата и время в Python не сообщают, какой часовой пояс они представляют. Если ваша программа обрабатывает наивную дату и время как время в формате UTC, а библиотека интерпретирует их как местное время, это может привести к ошибке смещения.
Для представления конкретных моментов времени в Python рекомендуется использовать даты и время в формате UTC. Модуль datetime также не поддерживает високосные секунды в обычной модели даты и времени.
Чтобы получить текущее время Unix в разных единицах измерения:
import time seconds = time.time() milliseconds = time.time_ns() // 1_000_000 nanoseconds = time.time_ns()
Используйте целые наносекунды, если вам нужно сохранить значение временной метки базового API без предварительного преобразования в число с плавающей запятой.
Для измерения прошедшего времени используйте:
import time
start = time.monotonic_ns()
# Выполните здесь какую-нибудь работу.
elapsed_ns = time.monotonic_ns() - start
elapsed_seconds = elapsed_ns / 1_000_000_000
print(f"Elapsed: {elapsed_seconds:.6f} seconds")
Суффикс _ns указывает на единицу измерения. Это не гарантирует, что часы сами измеряют время с точностью до наносекунды.
JavaScript
API Date использует миллисекунды с начала эпохи Unix.
const timestampSeconds = 1483228800; const date = new Date(timestampSeconds * 1000); console.log(date.toISOString()); // 2017-01-01T00:00:00.000Z
Если ваш API предоставляет секунды в формате Unix, умножьте их на 1000 перед созданием Date.
Если вы хотите преобразовать Date.now() в секунды в формате Unix:
const timestampSeconds = Math.floor(Date.now() / 1000);
Для дат до или после начала эпохи значения даты в JavaScript также могут представлять отрицательные временные метки. В обычном Date представлении используются миллисекунды, а не наносекунды; если вам нужно работать с наносекундами текущей эпохи, то это число слишком велико для того, чтобы тип Number в JavaScript мог точно представить каждое целое число. Используйте BigInt или соответствующий API времени с высоким разрешением для таких значений.
PostgreSQL
PostgreSQL может преобразовывать значения секунд эпохи и временных меток:
SELECT to_timestamp(1483228800);
Возвращает соответствующую временную метку с часовым поясом. Отображаемое смещение зависит от настроенного часового пояса сеанса PostgreSQL.
Обратное преобразование:
SELECT EXTRACT( EPOCH FROM TIMESTAMPTZ '2017-01-01 00:00:00+00' );
Результат:
1483228800.000000
В документации PostgreSQL to_timestamp() и EXTRACT(EPOCH ...) описаны в разделе функции для работы с датами и временем.
Одна из распространенных ошибок при работе с базами данных — путаница между timestamp without time zone и timestamp with time zone (timestamptz в PostgreSQL). Временная метка без указания часового пояса не позволяет однозначно определить момент времени. PostgreSQL хранит временные метки с указанием часового пояса в формате UTC и отображает их в соответствии с настройками часового пояса сеанса.
Для событий, произошедших в определенный момент времени, используйте однозначное представление времени. Если вам нужно сохранить исходное название часового пояса или введенное пользователем местное время, сохраните эту информацию отдельно.
9. Можно ли использовать временную метку Unix в качестве первичного ключа базы данных?
Предположим, вы создали такую таблицу:
CREATE TABLE events ( id BIGINT PRIMARY KEY, event_name TEXT NOT NULL );
И вы решаете, что id будет содержать текущую временную метку Unix.
Это может сработать в системе с очень низкой нагрузкой, если вы используете секунды и гарантируете не более одной вставки в секунду. Но как только у вас появятся пользователи, работающие одновременно, в одну и ту же секунду могут произойти два события. Оба события попытаются вставить один и тот же первичный ключ, и одна вставка завершится ошибкой.
Можно улучшить ситуацию, используя миллисекунды или наносекунды. Это снижает риск коллизии из-за ограниченного разрешения, но не гарантирует уникальность.
Существует несколько причин:
- Две операции могут выполняться в рамках одного тактового интервала.
- Часы могут повторять значение после коррекции или события, связанного с високосным годом.
- Два разных сервера могут сообщать об одном и том же времени.
- Часы на двух серверах могут показывать разное время.
- Системные часы могут быть переведены назад, поэтому более поздняя операция может получить меньшую временную метку.
Временная метка — отличный метаданные для указания времени. Но она не является надежным идентификатором.
Что следует использовать вместо нее?
Для обычной идентификации в базе данных используйте сгенерированную базой данных последовательность или подходящий уникальный идентификатор, например UUID.
Если вам нужно сохранить и время события, сохраняйте оба значения:
CREATE TABLE events ( id UUID PRIMARY KEY, occurred_at TIMESTAMPTZ NOT NULL, event_name TEXT NOT NULL );
Теперь id идентифицирует событие, а occurred_at фиксирует, когда оно произошло.
Если вам нужен строгий порядок событий, особенно в нескольких сервисах, одних временных меток может быть недостаточно. В зависимости от системы более подходящими могут оказаться последовательность в базе данных, смещение в журнале или механизм распределенного упорядочивания.
10. Проблема 2038 года
У эпохи Unix есть еще один известный пограничный случай: проблема 2038 года.
В некоторых старых системах секунды, прошедшие с начала эпохи, хранились в виде 32-битного целого числа со знаком.
Наибольшее 32-разрядное целое число со знаком:
2147483647
В интерпретации Unix-секунд это означает:
2038-01-19 03:14:07 UTC
Через секунду значение превысит диапазон этого целого числа. Программа, использующая такое представление, может столкнуться с переполнением или другим сбоем.
В этом кодировании 32-битное представление со знаком не может отображать даты ранее 1901-12-13 20:45:52 UTC . GNU описывает этот диапазон и более широкий диапазон, доступный в современных 64-битных системах.
Современные системы обычно используют более широкие представления временных меток, но не все приложения, форматы файлов, встроенные устройства, драйверы баз данных и сетевые протоколы были обновлены.
Для долгоживущих систем проверьте фактический целочисленный тип, формат сериализации и диапазон API. Простая модернизация сервера не исправит автоматически старую 32-битную временную метку, хранящуюся в файле или базе данных.
11. Строки с датами, временные метки и планирование на будущее
Последний момент заслуживает особого внимания: не каждую проблему, связанную со временем, можно решить с помощью временной метки Unix.
Временная метка хорошо подходит для обозначения конкретного момента времени.
Но рассмотрим следующие запросы:
- Отправить отчет завтра в 9:00.
- Выполнять задачу каждый день в 2:00.
- Запланировать встречу на 9:00 в первый понедельник каждого месяца.
- Напоминать мне каждый будний день в 8:30 по индийскому времени.
Это календарные правила. Для них нужны дата, местное время, часовой пояс, а иногда и правило повторения.
Предположим, пользователь запланировал встречу на 9:00 в Нью-Йорке в марте следующего года. Если вы сохраните только одну временную метку, то сохраните рассчитанное время, но не обязательно то, которое изначально планировал пользователь. Если до этого момента изменятся правила часовых поясов, то запланированное местное время и ранее рассчитанное время могут не совпадать.
Для повторяющегося расписания в будущем сохраните местное время и соответствующий идентификатор часового пояса, например America/New_York, вместе с правилом повторения. Разрешите каждое повторение, используя правила для текущего часового пояса.
Для события, которое уже произошло, сохранить момент события в формате UTC, как правило, не составляет труда.
Предпочтительнее использовать однозначный текстовый формат
Хороший формат интернет-временной метки:
2026-10-20T10:15:10Z
Z указывает на время по Гринвичу.
Время с явным смещением также однозначно:
2026-10-20T10:15:10+05:30
Эти два примера представляют разные моменты времени, поскольку в одном случае смещение от UTC равно нулю, а в другом — на пять часов и тридцать минут больше, чем в UTC.
RFC 3339 определяет стандартизированный интернет-формат даты и времени, призванный устранить путаницу между различными представлениями.
Не отправляйте дату и время в чистом виде, например 2026-10-20 10:15:10 , через границу системы, если вы явно не указали, какой часовой пояс они представляют.
12. Практический контрольный список для производственных систем
При разработке приложения, которое записывает или вычисляет время, соблюдение этих правил поможет избежать многих распространенных ошибок.
Используйте задокументированные единицы измерения. Указывайте, в каких единицах измерения представлена каждая временная метка: секундах, миллисекундах, микросекундах или наносекундах. Не полагайтесь на количество цифр.
Используйте UTC для указания времени событий. Сохраняйте и передавайте данные в едином формате, а затем конвертируйте их в местное время для отображения.
Используйте подходящие типы времени. В базе данных лучше использовать тип timestamp с четкой семантикой часового пояса, а не неоднозначную локальную дату и время. Для повторяющихся расписаний сохраняйте местное время и правила часового пояса, определяющие расписание.
Используйте монотонные часы для измерения длительности. Таймауты, бенчмарки и измерения производительности не должны зависеть от корректировки времени на настенных часах.
Не используйте время в качестве идентификатора. Используйте последовательность или уникальный идентификатор для идентификации, а временную метку — для указания времени события.
Знайте ограничения своих часов. Форматирование в наносекундах не означает точность до наносекунды. Настенные часы могут идти в обратном направлении. Разные устройства могут по-разному определять текущее время.
Тестируйте границы. Проверьте преобразование единиц измерения временных меток, даты до начала эпохи, 32-битные ограничения, переход на летнее время и то, как ваша система обрабатывает синхронизацию часов. Системы, которым требуется точное отслеживание времени, также должны проверять, как их реальные часы и среда выполнения реагируют на високосные секунды.
Разделяйте форматы и семантику. Такое число, как 1790000000 , не является самоописательным. Такое значение, как 2026-10-20T10:15:10Z , явно указывает на календарное представление и его связь с UTC.
Распространенные заблуждения о временных метках Unix
Самая большая ошибка, которую допускают люди, — это отношение к временной метке Unix как к идеальному счетчику времени. Это удобный способ представления времени, но он не гарантирует, что часы идут правильно, что значения уникальны или что время всегда движется вперед.
Вот самые распространенные заблуждения, объяснение, почему они неверны, и что делать вместо этого.
Заблуждение 1: каждая временная метка Unix уникальна
Неверно. Временная метка не является уникальным идентификатором.
Предположим, что в течение одной секунды поступили два запроса:
import time request_a = int(time.time()) request_b = int(time.time()) print(request_a == request_b) # True вполне возможно
Оба запроса могут получить одну и ту же временную метку. Это нормально, это не ошибка.
Использование миллисекунд или наносекунд снижает вероятность коллизий, но не гарантирует уникальность. Две машины также могут независимо сгенерировать одну и ту же временную метку.
Более эффективный подход: используйте UUID или идентификатор, сгенерированный базой данных, для идентификации записей. Храните временную метку отдельно, чтобы фиксировать время каждого события.
Заблуждение 2: временная метка Unix — это просто UTC, преобразованное в число.
Не совсем так. Unix-время и UTC тесно связаны, но это не одно и то же.
Временные метки Unix традиционно отсчитывают секунды от 1970-01-01 00:00:00 UTC. Время POSIX не представляет дополнительные секунды в виде отдельных обычных временных меток. Это приводит к несоответствию, когда в UTC появляется секунда с меткой 23:59:60. Разные реализации часов могут по-разному обрабатывать эту границу.
Например, во время дополнительной секунды 31 декабря 2016 года UTC показывало следующие значения:
23:59:59 23:59:60 <- дополнительная секунда 00:00:00
Обычные временные метки Unix не дают переносимого и однозначного целого числа для всех трех значений.
Кроме того, грядут изменения. Международный орган по стандартизации в области метрологии BIPM принял решение о том, что непрерывное время в формате UTC вступит в силу 20 мая 2027 года, что устранит необходимость в корректировке високосных секунд в соответствии с новыми правилами. Исторические временные метки и существующие программные соглашения по-прежнему будут иметь значение.
Более правильный подход: рассматривайте временные метки Unix как специфическое представление времени и проверяйте шкалу времени и правила високосных секунд при работе с системами, чувствительными к точности.
Заблуждение 3: временная метка Unix всегда увеличивается.
Неправильный. Настенные часы в системе могут идти в обратном направлении.
Временная метка Unix обычно берется из системных часов реального времени. Эти часы могут быть скорректированы, если они показывали неправильное время, если их изменил администратор или если их скорректировала служба синхронизации времени.
Представьте себе такую последовательность:
10:00:05 Первое измерение 10:00:03 Второе измерение
Второе показание более раннее, хотя вы сделали его позже. Возможно, часы были скорректированы между двумя показаниями.
В документации Linux описано такое поведение для CLOCK_REALTIME. Также есть CLOCK_MONOTONIC, показания которых не меняются в обратном направлении.
Более эффективный подход: используйте временные метки настенных часов, чтобы фиксировать время событий. Используйте монотонные часы для измерения продолжительности и установки таймаутов.
import time
start = time.monotonic()
# Выполните здесь какую-нибудь операцию.
elapsed = time.monotonic() - start
print(f"Took {elapsed:.3f} seconds")
Не используйте time.time() в качестве секундомера, если есть монотонные часы.
Заблуждение 4: число в метке времени указывает на единицу измерения.
Неверно. В метках времени Unix могут использоваться секунды, миллисекунды, микросекунды или наносекунды. Число само по себе не определяет единицу измерения.
Один и тот же момент времени может быть представлен как:
| Единица измерения | Примерное значение |
|---|---|
| Секунды | 1483228800 |
| Миллисекунды | 1483228800000 |
| Микросекунды | 1483228800000000 |
| Наносекунды | 1483228800000000000 |
Рассмотрим JavaScript:
new Date(1483228800);
Это не 1 января 2017 года. JavaScript Date API интерпретирует число как количество миллисекунд, прошедших с начала эпохи, поэтому результат относится к январю 1970 года.
Используйте правильную единицу измерения:
new Date(1483228800 * 1000).toISOString(); // "2017-01-01T00:00:00.000Z"
Более правильный подход: явно указывать единицы измерения. Назовите поля timestamp_seconds или created_at_ms, а не просто timestamp.
Заблуждение 5: точность до наносекунды означает точность до наносекунды.
Неправильный. Точность, разрешающая способность и аккуратность — это разные понятия.
| Термин | Значение |
|---|---|
| Точность | Насколько точно может быть представлено значение |
| Разрешение | Наименьшая разница, которую могут показать часы |
| Точность | Насколько близко показанное время к эталонному |
Часы могут показывать:
10:15:10.123456789
Это не доказывает, что они показывают точное время с точностью до наносекунды. Их показания могут значительно отличаться от времени по Гринвичу.
Даже два последовательных показания часов могут возвращать одно и то же значение, если разрешение часов ограничено. В документации по часам в Linux четко разграничивается разрешение часов и возвращаемые ими значения времени.
Более правильный подход: если время или синхронизация имеют значение, проверьте фактическое разрешение часов, статус синхронизации и границы погрешности. Не думайте, что чем больше знаков после запятой, тем точнее часы.
Заблуждение 6: временная метка Unix меняется при смене часового пояса.
Неправильный. Временная метка не содержит информации о часовом поясе.
Одна и та же временная метка может отображаться по-разному в зависимости от выбранного часового пояса.
Например, временная метка 1483228800 соответствует моменту, который отображается как:
| Часовой пояс | Местная дата и время |
|---|---|
| UTC | 2017-01-01 00:00:00 |
| Москва | 2017-01-01 03:00:00 |
| Нью-Йорк | 2016-12-31 19:00:00 |
Момент времени тот же. Отображаемое календарное время меняется.
Вот почему следующие команды могут выдавать разные даты или время:
TZ=UTC date -d '@1483228800' TZ=Europe/Moscow date -d '@1483228800'
В этих примерах используется GNU date.
Более правильный подход: сохраняйте однозначное время события, используйте UTC в качестве общего стандарта для журналов и API и конвертируйте в соответствующий местный часовой пояс для отображения. Для будущих встреч сохраняйте соответствующие правила часовых поясов, а не только рассчитанную временную метку.
Заблуждение 7: в сутках ровно 86 400 секунд.
Неправильный. Обычный Unix-день часто считается равным 86 400 секундам, но продолжительность местного календарного дня не всегда такая.
Из-за перехода на летнее время продолжительность местного дня может составлять 23 или 25 часов. Кроме того, из-за високосных секунд в UTC в некоторых днях появляется дополнительная секунда.
Например, ежедневное задание, запланированное на 9:00 по местному времени, не обязательно запускать, прибавив ровно 86 400 секунд к предыдущему времени выполнения. Это может привести к смещению времени выполнения задачи при переходе на летнее время.
Более эффективный подход: используйте планирование с учетом календаря для повторяющихся событий. Используйте расчет времени для определения продолжительности. Это разные операции.
Заблуждение 8: вычитание двух временных меток всегда дает точное значение прошедшего времени.
Неправильный. При вычитании получается разница между двумя зафиксированными показаниями настенных часов, которая может не совпадать с фактической продолжительностью.
Например:
Начальное показание: 1000 Конечное показание: 995
Разница составляет -5 секунд, но операция не выполнялась в обратном направлении. Настенные часы изменились.
Даже если часы идут только вперед, из-за настроек синхронизации или разницы в показаниях часов на разных устройствах расчетная продолжительность может быть неточной.
Более правильный подход: используйте монотонные часы при измерении времени выполнения, задержки или тайм-аутов. Используйте настенные часы, когда вам нужны календарные даты и время событий.
Если вы сравниваете временные метки с двух серверов, помните, что их часы могут не совпадать.
Заблуждение 9: даты до 1970 года не могут иметь временные метки Unix.
Неправильный. Временные метки могут быть отрицательными.
Эпоха Unix — это точка отсчета, а не начало времени.
| Временная метка Unix | дата и время в формате UTC |
|---|---|
-2 | 1969-12-31 23:59:58 |
-1 | 1969-12-31 23:59:59 |
0 | 1970-01-01 00:00:00 |
1 | 1970-01-01 00:00:01 |
Отрицательная временная метка просто обозначает время до начала отсчета.
Однако поддерживаемые диапазоны дат зависят от языка программирования, операционной системы, базы данных и базового типа данных.
Более правильный подход: при работе с историческими датами проверяйте поддерживаемый диапазон всей системы, а не предполагайте, что все временные метки должны быть положительными.
Заблуждение 10: проблема 2038 года затрагивает все временные метки Unix.
Неправильный. Это влияет на системы, в которых секунды Unix хранятся в 32-битном целом числе со знаком, а не на все системы, использующие временные метки Unix.
Максимальное значение 32-битного целого числа со знаком — 2147483647, что соответствует:
2038-01-19 03:14:07 UTC
Через секунду это представление переполнится. Программное обеспечение, которое на него опирается, может дать сбой или выдать неверные даты.
Современные 64-битные системы обычно используют более широкие представления времени, но устаревшее программное обеспечение, встроенные устройства, форматы файлов или поля в базах данных все еще могут иметь ограничения.
Более правильный подход: проверьте фактический тип данных и формат сериализации. Не думайте, что переход на 64-битную операционную систему исправит ситуацию с временными метками, которые уже хранятся в 32-битном формате.
Заблуждение 11: временной метки достаточно, чтобы указать любую дату или время.
Неправильный. Временная метка хорошо подходит для фиксации конкретного момента, но некоторые требования описывают календарные правила, а не конкретные моменты.
Рассмотрим эти два требования:
- «Этот платеж был осуществлен в 9:00 утра».
- «Отправляйте отчет в 9:00 утра каждый понедельник в Индии».
В первом случае описывается произошедшее событие. Во втором — повторяющееся расписание.
Одна временная метка не может полностью описать повторяющееся расписание. Для сохранения заданного поведения необходимо указать местное время, часовой пояс и правило повторения.
Более эффективный подход: используйте временные метки для обозначения моментов событий. Используйте типы даты и времени с поддержкой календаря и идентификаторы часовых поясов для будущих встреч и повторяющихся расписаний.
Пять правил, которые стоит запомнить
| Заблуждение | Что нужно помнить |
|---|---|
| Временные метки уникальны | Используйте идентификаторы для идентификации записей. |
| Unix-время — это точное время по Гринвичу | Високосные секунды указывают на различия между шкалами времени. |
| Часы идут только вперед | Настенные часы можно перевести назад. |
| Чем больше цифр, тем выше точность | Точность не гарантирует правильность. |
| Для решения любой проблемы, связанной со временем, нужна временная метка | Для определения продолжительности и составления календарных планов нужны разные инструменты. |
Главный урок прост: временная метка — это представление времени, а не гарантия его точности. Как только вы поймете это различие, вам станет проще объяснять и предотвращать большинство неожиданных ситуаций, связанных с временными метками Unix.
Выводы
Позвольте нам подытожить то, что мы узнали о временных метках Unix:
- Временная метка Unix — это числовое представление времени относительно эпохи Unix.
- Временная метка не является автоматически уникальным идентификатором.
- Единица измерения — это часть значения числа.
- UTC, Unix-время, атомное время и монотонные часы решают связанные, но разные задачи.
- Более точное представление не гарантирует более высокую точность часов.
- Високосные секунды показывают, что календарное время и обычное Unix-время не являются полностью взаимозаменяемыми. Даже несмотря на то, что с 20 мая 2027 года будет действовать непрерывное время по Гринвичу, эти различия по-прежнему важны.
Никогда не используйте число, не зная, что оно означает. Это особенно актуально для времени. Код может скомпилироваться, база данных может принять значение, а API может вернуть вполне корректное целое число. Ошибка часто кроется в предположениях, а не в самом числе.
Как только вы разделите моменты времени, календарные правила, продолжительность, поведение часов, точность и идентификацию, временные метки Unix станут гораздо менее загадочными и их будет гораздо проще безопасно использовать.
