В октябре 28-я Генеральная конференция по мерам и весам решила, что с 20 мая 2027 года в UTC перестанут добавлять високосные секунды. База часовых поясов tzdb в выпуске 2026f от 27 октября откликнулась строкой «leap second tables no longer expire» и научила zic не смотреть на срок таблицы. При этом файл leap-seconds.list, который tzdb раздаёт вместе с зонами и который читают NTP-демоны, по-прежнему со сроком годности: строка #@ в нём говорит, что файл истекает 28 июня 2027 года. Будет ли IERS продлевать его после отмены, пока не сказал никто.
Я запустил классический ntpd, NTPsec и chrony с истёкшим файлом, со свежим под подменённой датой после 28 июня 2027 и с файлом, из которого убрана строка срока. Истёкший файл принимают все трое: ntpd и NTPsec загружают таблицу со смещением 37 секунд и пишут ошибку, chrony пишет предупреждение и файл не отвергает. Плохо кончается другой вариант. Файл без строки срока chrony 4.6.1 отвергает целиком, а оба ntpd либо не грузят его совсем, либо считают просроченным с 1900 или 1970 года.
Зачем у файла срок годности
О новой високосной секунде IERS объявляет примерно за полгода в бюллетене C, и leap-seconds.list перевыпускают раз в полгода. Строка #@ — срок годности файла: до этой даты таблица считается актуальной. Meinberg в своей базе знаний объясняет смысл так: после срока программа уже не может знать, не назначили ли новую секунду. Последний раз секунду вставили 1 января 2017 года, с тех пор UTC отстаёт от TAI на 37 секунд. Бюллетень C 72 от 6 июля 2026 года подтвердил, что в конце декабря секунды не будет. Действующий файл обновлён по этому бюллетеню и истекает 28 июня 2027.
Выпуск tzdb 2026f снял срок с таблиц внутри TZif-файлов (зоны right/*), как и просит RFC 9636: таблицы, опубликованные после отмены, «SHOULD NOT expire». Про сам leap-seconds.list в NEWS ни слова. Его выпускает IERS, tzdb только кладёт рядом. Ещё в 2023a скрипт leapseconds.awk в tzdb научили обрабатывать файл, «that never expires; this might be useful if leap seconds are discontinued». Значит, впереди один из двух вариантов: IERS продолжит продлевать #@ или выпустит файл без срока. Третий вариант уже идёт: файл в системе просто не обновили.
Есть и противоречие между источниками. Meinberg пишет, что ntpd 4.2.8 и новее «really check the expiration date of the leap second file, and stop using it after the file has expired». Если понимать это буквально, после 28 июня 2027 у всех, кто не обновит tzdata, демон потеряет таблицу. Поэтому я проверил, что делают сами демоны.
Как проверял
Пакеты брал из архивов: NTPsec 1.2.3 и chrony 4.6.1 из Debian 13, ntpd 4.2.8p15 из Ubuntu 22.04. Распаковывал dpkg-deb -x в одноразовом контейнере и запускал без root. Проверку getuid() у ntpd обходил маленьким шимом через LD_PRELOAD. Часы демоны не ставили: в контейнере нет CAP_SYS_TIME. Дату после срока подставлял libfaketime внутри контейнера, часы хоста никто не трогал. chrony проверяет срок файла только после синхронизации, поэтому второй chronyd на loopback служил ему сервером.
Файлы были такие:
-
свежий из tzdb, срок 2027-06-28;
-
из tzdata2023c, срок 2023-12-28;
-
свежий под датами 2027-07-01 и 2030-01-01;
-
свежий без строки
#@, с прежней подписью#hи без неё; -
свежий со сроком 2100-01-01 и пересчитанной подписью.
Подпись #h — это SHA-1 от цифр строк #$, #@ и строк данных. Чтобы не сломать её по ошибке, я сначала пересчитал хеш исходного файла своим скриптом, он совпал с #h.
Истёкший файл: ошибка в журнале, таблица загружена
Классический ntpd с файлом 2023 года:
leapsecond file ('/work/e2/old.list'): expired 1042 days agoleapsecond file ('/work/e2/old.list'): good hash signatureleapsecond file ('/work/e2/old.list'): loaded, expire=2023-12-28T00:00:00Z last=2017-01-01T00:00:00Z ofs=37ntpq: leap=11, leapsec=201701010000, expire=202312280000
Таблица загружена, ntpq отдаёт дату последней секунды и срок. NTPsec пишет почти то же самое: «expired less than 1042 days ago» и loaded … ofs=37. Со свежим файлом под датой 1 июля 2027 года оба пишут «expired 3 days ago», под 1 января 2030 года — «918 days ago», и оба загружают таблицу.
Значит, слова Meinberg «stop using it» на этих версиях про потерю таблицы не говорят. В коде NTPsec загрузка вообще не смотрит на срок. Срок нужен в другом месте: функция leapsec_add_dyn разрешает добавить секунду, объявленную вышестоящими серверами, только когда таблица истекла. Истёкший файл перестаёт быть последним словом о будущих секундах, но прошлые 37 секунд остаются. Сам приём объявления от сервера я не проверял: для этого нужен вышестоящий сервер, который выставляет флаг секунды.
Цена истёкшего файла — шум. По коду NTPsec после срока пишет ошибку уровня LOG_ERR раз в сутки, и это условие не кончается. Живой журнал из бага Ubuntu на Launchpad #2144453 так и выглядит: «expired less than 68 days ago», через сутки 69, ещё через сутки 70. chrony после синхронизации пишет предупреждение Leap second list … needs update, по исходнику leapdb.c не чаще двух раз в сутки. До синхронизации, в режиме chronyd -Q, он молчит, и моя первая проба это пропустила.
|
файл |
ntpd 4.2.8p15 |
NTPsec 1.2.3 |
chrony 4.6.1 |
|---|---|---|---|
|
свежий, срок 2027-06-28 |
загружен, ofs=37 |
загружен, ofs=37 |
молча |
|
истёк 2023-12-28 |
ошибка, загружен, ofs=37 |
ошибка, загружен, ofs=37 |
WARN «needs update» |
|
свежий, дата 2027-07-01 |
ошибка, загружен, ofs=37 |
ошибка, загружен, ofs=37 |
WARN «needs update» |
|
без |
signature mismatch, не загружен |
signature mismatch, не загружен |
отвергнут |
|
без |
загружен, срок 1900-01-01 |
загружен, срок 1970-01-01 |
отвергнут |
|
срок 2100-01-01, подпись пересчитана |
загружен |
загружен |
молча |
Файл без срока: chrony его отвергает
У chrony 4.6.1 разбор leapseclist заканчивается проверкой:
/* Make sure the file looks sensible */ if (!feof(f) || !lsl_updated || !lsl_expiry) goto error;
Нет строки #@, значит lsl_expiry остаётся нулём, и файл объявляется негодным. В журнале это выглядит так, в обоих вариантах файла без срока:
Failed to parse leap seconds list /work/e3/noexp.listLeap second list /work/e3/noexp.list failed check, ignoringSelected source 127.0.0.1
Время chrony синхронизирует дальше, но работает так, будто директивы leapseclist в конфиге нет. Файл без срока с подписью, пересчитанной уже без строки #@, я не собирал. chrony отверг бы и его: условие выше проверяет только наличие строки срока, подпись тут ни при чём.
У ntpd картина зависит от подписи. Если просто вырезать #@, прежняя подпись перестаёт сходиться, оба демона пишут signature mismatch и таблицу не грузят: в выводе ntpq пропадают leapsec и expire. Без подписи файл загружается, но срок становится нулевым, то есть началом эпохи. У классического ntpd это 1900 год и «expired 46328 days ago», у NTPsec 1970 год и «expired less than 20761 days ago». Для ntpd такой «вечный» файл ничем не лучше просроченного: та же ежедневная ошибка, только с первого дня.
Без жалоб все три приняли один вариант: далёкий срок в #@ и подпись, пересчитанная вместе с ним. Одна деталь: классический ntpq показал срок 2100 года как expire=196311251731. Это переполнение 32-битной NTP-эры, которая кончается 7 февраля 2036 года. Сам демон дату загрузил верно, в журнале expire=2100-01-01.
Какой файл стоит у вас сейчас
Срок файла в системе показывает одна команда. В #@ записаны секунды от 1900 года, отсюда поправка:
date -u -d @$(( $(awk '/^#@/{print $2}' /usr/share/zoneinfo/leap-seconds.list) - 2208988800 )) +%F
Ту же проверку я прогнал по пакетам tzdata из индексов Debian, Ubuntu и Alpine на 3 ноября 2026 года. Для Debian брал самую новую версию из релизного кармана и -updates, для Ubuntu ещё и из -security, для Alpine — из ветки main:
|
выпуск |
tzdata |
срок |
|---|---|---|
|
Debian 11 |
2024a |
2024-12-28, истёк |
|
Debian 12 |
2026b |
2026-12-28 |
|
Debian 13 |
2026f |
2027-06-28 |
|
Ubuntu 20.04 |
2025b |
2025-12-28, истёк |
|
Ubuntu 22.04, 24.04, 26.04 |
2026e |
2027-06-28 |
|
Ubuntu 25.10 |
2026b |
2026-12-28 |
|
Alpine 3.19 |
2025b |
2025-12-28, истёк |
|
Alpine 3.20 |
2026b |
2026-12-28 |
|
Alpine 3.21–3.23, edge |
2026e, 2026f |
2027-06-28 |
Три выпуска уже несут истёкший файл, ещё у трёх он истечёт через восемь недель, 28 декабря. Если образ собран только из релизного кармана, без -updates, файл истёк у всех Ubuntu, кроме 26.04. Баг #2144453 в марте жаловался, что истекли все LTS. Для 22.04 и 24.04 это уже исправлено пакетом 2026e, для 20.04 нет.
Что из этого следует
Если IERS продолжит продлевать срок раз в полгода, ничего не меняется: обновляйте tzdata, как раньше. Если перестанет и файл просто истечёт 28 июня 2027 года, в моих прогонах ntpd и NTPsec с таким файлом держали таблицу со смещением 37 секунд, а chrony его не отвергал. Останется ежедневная ошибка в журнале ntpd и NTPsec и предупреждение у chrony, не чаще двух раз в сутки. Пока кто-нибудь не выпустит файл с продлённым сроком, правило мониторинга на эти строки будет срабатывать впустую.
Риск в другом варианте. Если выйдет «вечный» файл без строки #@, после обновления tzdata chrony 4.6.1, который читает этот файл директивой leapseclist, файл отвергнет и останется без таблицы, а у ntpd даст ошибку с первого дня. С директивой leapsectz chrony берёт секунды из зоны tzdb, и этот файл его не касается. После такого обновления стоит поискать в журнале chrony Failed to parse leap seconds list. Для тех, кто будет готовить такой файл: без жалоб все три демона загрузили только далёкий срок с пересчитанной подписью. Если важен вывод ntpq у классического ntpd, срок лучше держать до 2036 года.
Чего я не проверял: синхронизацию ntpd с настоящим вышестоящим сервером и приём объявленной им секунды при истёкшей таблице, значение tai= через ntpq (ntpd публикует его только после синхронизации) и другие версии демонов. Все прогоны сделаны 1 и 3 ноября 2026 года в контейнере на образе python:3.15 (Debian 13).
ссылка на оригинал статьи https://habr.com/ru/articles/1103394/