Как вы хорошо знаете, в Unix-системах мы измеряем время как количество секунд, прошедших с «эпохи»: 00:00:00 UTC 1 января 1970 года. Немало людей сильно разозлилось из-за этого, да и вообще, общественное мнение сочло это ошибкой.
Во-первых, это определение основано не на чём-то разумном, например, на объективной частоте колебаний атома цезия-133, а на удобной доле времени полного оборота одного большого камня вокруг собственной оси.
Во времени Unix каждый день гарантированно состоит из 86400 секунд и мы притворяемся, что это число равномерно увеличивается. Когда оказывается, что вышеупомянутый камень на самом деле вращался дольше, чем удобно для нас, и нам нужно добавить секунду координации, то мы просто притворяемся, что этого не было, а механизм меток времени не идентифицирует уникальный момент времени.
Ещё один аспект, который продолжает вызывать проблемы, когда мы пытаемся считать секунды, заключается в том, что мы сталкиваемся с проблемами хранения и описания данных, потому что, как оказалось, компьютеры не так уж хорошо справляются с числами. Не говоря уж об "эпохальном сбое".
Как же мы к этому пришли? Всё началось в 1971 году, когда в в первом издании руководства для программиста Unix было дано определение времени Unix как "времени с 00:00:00 1 января 1971 года, измеряемого в шестидесятых долях секунды":
Именно так, изначально эпохой Unix было
1971-01-01T00:00:00
. Вы спросите, какого часового пояса? Ну, это точно не был «UTC», потому что он заменит GMT в качестве стандартного времени только в 1972 году. Во-вторых, обратите внимание, что время измерялось в 1/60 секунды, а не в секундах. Зачем же так сделали?Вспомним, что в то время Unix разрабатывался в США на PDP-11. Эти системы имели часы линейного времени (Line-Time Clock, LTC), использующие частоту питания переменного тока для генерации прерывания для процессора. Тогда это прерывание использовалось для обновления системных часов.
Забавно здесь то, что эта частота прерываний зависит от частоты в сети источника питания. В основной части Азии и Европы эта частота равна 50 Гц, однако в США Westinghouse Electric Corporation (конкурент Томаса Эдисона и General Electric; любопытный факт: позже Westinghouse приобрела CBS, а затем была куплена Viacom) заметила, что использовавшиеся тогда дуговые лампы с угольным электродом меньше мерцают при 60 Гц, а поэтому стандартизировала эту частоту.
(Странный побочный эффект этого различия между США и Европой заключается в том, что в Японии используются обе частоты: на западе, где первые генераторы были куплены у немецкой AEG и установлены в Токио, частота в сети равна 50 Гц; на востоке, в Осаке, были установлены генераторы G.E., и используются 60 Гц. В результате этого в стране теперь работает множество высоковольтных линий постоянного тока для преобразования электричества между двумя регионами!)
Электросеть Японии
Итак, в первом издании UNIX измерял время на этих 60 Гц с хранением в 32-битных integer, таким образом имея возможность учитывать только
2^32 / 60 тактов/с * 60 с/мин * 60 мин/ч * 24 ч/д * 365 д/г = 2,3 лет
, как и написано в руководстве. Несколько позже в том же 1971 году измерению времени было дано другое определение, оно учитывалось в секундах и могло описывать до 136 лет. Датой же эпохи Unix был достаточно произвольно выбран 1970 год (вопреки распространённому заблуждению о том, что она обозначает дату рождения Unix):«В то время у нас не было плёночных накопителей, работала пара файловых систем, и мы постоянно меняли начало отсчёта времени. Поэтому в конце концов мы сказали: давайте выберем что-то одно, что достаточно долго не будет переполняться. Нас вполне устроил 1970 год», — сказал он.
— Дэннис Ритчи, Wired
(Истинная дата рождения Unix приходится примерно на 1969 год, когда Кен Томпсон портировал "Space Travel" на PDP-7, поэтому легко понять, почему эпоха Unix кажется обозначением рождения Unix.)
Секунды координации
Итак, теперь у нас есть 32-битный счётчик секунд от эпохи, который равномерно увеличивает значение с частотой 1 Гц и гарантирует нам, что будет насчитывать ровно 86400 секунд в любой 24-часовой период. Однако наш космический булыжник отсчёта замедляется в своём вращении, поэтому время от времени это число нужно изменять.
Для этого Международная служба вращения земли (IERS) отправляет всем Повелителям Времени электронные письма, сообщающие, должна или нет добавляться секунда координации:
Сегодня мы не можем винить время эпохи Unix за то, что оно изначально не учитывало секунды координации, потому что их не существовало до 1972 года. С тех пор (на 2022 год) произошло уже 27 положительных секунд координации, последняя из которых была введена в конце 2016 года. Каждый раз, когда происходит секунда координации, время эпохи Unix просто притворяется, что этого не было, из-за чего две даты соответствуют одной метке времени эпохи (epoch-time.c):
From epoch to time, via gmtime(3) to strftime(3):
1483228798 2016-12-31T23:59:58
1483228799 2016-12-31T23:59:59
//здесь отсутствующая секунда координации
1483228800 2017-01-01T00:00:00
From time to epoch, via strptime(3) to mktime(3):
2016-12-31T23:59:58 is 1483228798
2016-12-31T23:59:59 is 1483228799
2016-12-31T23:59:60 is 1483228800
2017-01-01T00:00:00 is 1483228800 //здесь дублирующаяся метка времени
(И давайте не забывать обо всех отрицательных секундах координации и тот факт, что некоторые системы Unix определяют диапазон
tm_sec
как [0-61]
, учитывая мифическую «двойную секунду координации», которой на самом деле никогда не существовало.)Хорошо в этом то, что всё это соответствует требованиям POSIX:
4.16 Секунды после эпохи
Значение, которое аппроксимирует количество секунд, прошедшее после эпохи.
Соотношение между истинным временем суток и текущим значением секунд после эпохи точно не установлено.
Способ внесения любых изменений в значение секунд после эпохи для согласования с нужным соотношением с текущим временем зависит от реализации. В формате секунд после эпохи каждый день насчитывает ровно 86400 секунд.
Проблема 2038 года
Даже при равномерном отсчёте и игнорировании секунд координации используемый для измерения секунд после эпохи тип данных
time_t
неизбежно придёт к переполнению. Хорошо, что есть стандарты, которые спасут нас в этой ситуации! Пусть POSIX не решает проблемы, но хорошо в стандартах то, что их много и можно выбрать подходящий, не так ли? Ох, постойте, в чём же дело? Давайте разберёмся.Плохие новости: стандарты нас не спасут. Например, стандарт C гласит:
Диапазон и степень точности времени, представленного в clock_t и time_t, зависят от реализации.
Что ж, ладно. Имея 32-битный
time_t
, мы можем рассчитывать начиная с 1970 года примерно на 136 лет. Однако time_t
является знаковым 32-битным integer, то есть у нас есть всего 68 лет в обоих направлениях, из-за чего возникает так называемая "Проблема 2038 года".Она состоит в том, что самая большая дата, которую можно записать как
time_t
при помощи знакового 32-битного integer — это 2^31 - 1 = 2147483647
эпохи, или 2038-01-19T03:14:07Z:Это легко исправить, правда? Мы «просто» изменяем тип данных
time_t
на знаковый 64-битный integer, что даёт нам теоретический максимум даты эпохи 2^63-1 = 9223372036854775807
после 1 января 1970 года. Как любят говорить люди, это значение задаёт дату примерно на 292 миллиарда лет в будущем, или примерно в 22 раз больше приблизительного возраста Вселенной, поэтому официально может считаться Чьей-то Чужой Проблемой.Но — всегда есть «но», не правда ли? — действительно ли это произойдёт? Почему бы не попробовать и не проверить, как разные системы поведут себя, если мы передадим им для обработки не совсем логичные значения времени?
Оборот mktime(3)
Хотя время представлено в виде
time_t
, используется и другой популярный формат для записи разбитого на части времени — struct tm
, из которого можно получить time_t
, вызвав mktime(3)
. В POSIX зафиксированы следующие элементы struct tm
:int tm_sec Seconds [0,60].
int tm_min Minutes [0,59].
int tm_hour Hour [0,23].
int tm_mday Day of month [1,31].
int tm_mon Month of year [0,11].
int tm_year Years since 1900.
int tm_wday Day of week [0,6] (Sunday =0).
int tm_yday Day of year [0,365].
int tm_isdst Daylight Savings flag.
Забавно в описанных здесь диапазонах то, что они в лучшем случае являются… рекомендательными. В POSIX конкретно написано:
… исходные значения [...] компонентов не должны быть ограничены диапазонами, описанными в <time.h>.
Так что же произойдёт, если передать
mktime(2)
тип struct tm
со значениями вне этих диапазонов? Рассмотрим следующую программу:int main() {
struct tm t;
time_t epoch;
/* 2022-12-31 */
t.tm_year = 122; t.tm_mon = 11; t.tm_mday = 31;
/* 22:57 */
t.tm_hour = 22; t.tm_min = 57;
t.tm_sec = 3785;
if ((epoch = mktime(&t)) == -1) {
err(EXIT_FAILURE, "mktime");
}
(void)printf("%s", ctime(&epoch));
return 0;
}
$ cc -Wall -Werror -Wextra t.c
$ ./a.out
Sun Jan 1 00:00:05 2023
$
Здесь перед присвоением
tm_sec
значения наш struct tm
задавал дату December 31st, 22:57:00, 2022. Но потом мы присвоили tm_sec
значение 3785
, то есть 1 час, 3 минуты и 5 секунд. Это приводит к тому, что выполняется инкремент tm_min
на 3 минуты, что приводит к увеличению tm_hour
на единицу. Затем мы ещё раз выполняем инкремент tm_hour
(из оставшихся от tm_sec
3600 секунд), что приводит к оборачиванию этого значения на 00
и необходимости инкремента tm_mday
. Теперь tm_mday
оборачивается до 01
с инкрементом tm_year
, и в результате мы получаем дату 2023-01-01T00:00:05
.Такая нормализация меток времени становится ещё более запутанной, когда мы добавляем отрицательные значения (например,
tm_mday = -1
означает предыдущий день tm_mon - 1
) или (снова) при участии секунд координации, что может стать причиной головной боли. Лучше избегать такой ситуации.Забавная date(1)
Давайте рассмотрим даты эпохи в момент или рядом с эпохой. Проигнорируем тот факт, что время эпохи до 1972 года определено не очень чётко, поскольку время эпохи по определению считается в UTC, но (см. выше) UTC стандартизировали только в 1972 году. Ну, мы поступим точно так же, как Unix поступает с секундами координации, и притворимся, что это нас не волнует.
Отобразить произвольную дату при помощи команды
date(1)
довольно легко. Достаточно просто передать дату в формате CCyymmddHHMM.SS
. Если только вы не работаете в Linux, где date(1)
GNU хочет использовать, наверное, самый выбешивающий формат (MMDDhhmmCCYY.ss
). Зачем вставлять год между минутами и секундами?Однако использование любого из этих форматов всё равно бесполезно для наших целей, потому что в определённый момент нам нужно будет выйти за пределы годов из четырёх цифр, поэтому давайте укажем непосредственно секунды после эпохи ("
-r <seconds>
" для date(1)
в BSD, "--date @<seconds>
" для date(1)
в GNU).Отобразить даты в момент эпохи или рядом с ней довольно просто:
NetBSD / FreeBSD / macOS:
$ date -r 0
Thu Jan 1 00:00:00 UTC 1970
$ date -r -1
Wed Dec 31 23:59:59 UTC 1969
$ date -r 1
Thu Jan 1 00:00:01 UTC 1970
Linux:
$ date --date @0
Thu Jan 1 12:00:00 AM UTC 1970
$ date --date @-1
Wed Dec 31 11:59:59 PM UTC 1969
$ date --date @1
Thu Jan 1 12:00:01 AM UTC 1970
OmniOS (с использованием даты GNU)
$ date -r 0
January 1, 1970 at 12:00:00 AM UTC
$ date -r -1
December 31, 1969 at 11:59:59 PM UTC
$ date -r 1
January 1, 1970 at 12:00:01 AM UTC
(
date(1)
GNU, использующая AM/PM вместо 24 часов, раздражает, ну да ладно.)Давайте посмотрим, что произойдёт, если мы попробуем проверить 32-битный
time_t
. Как говорилось выше, проблема 2038 года возникнет в 2147483648
/ -2147483649
эпохи:netbsd$ date -r 2147483647
Tue Jan 19 03:14:07 UTC 2038
netbsd$ date -r 2147483648
Tue Jan 19 03:14:08 UTC 2038
netbsd$ date -r -2147483648
Fri Dec 13 20:45:52 UTC 1901
netbsd$ date -r -2147483649
Fri Dec 13 20:45:51 UTC 1901
linux$ date --date @2147483647
Tue Jan 19 03:14:07 AM UTC 2038
linux$ date --date @2147483648
Tue Jan 19 03:14:08 AM UTC 2038
linux$ date --date @-2147483648
Fri Dec 13 08:45:52 PM UTC 1901
linux$ date --date @-2147483649
Fri Dec 13 08:45:51 PM UTC 1901
omnios$ date -r -2147483648
December 13, 1901 at 08:45:52 PM UTC
omnios$ date -r -2147483649
date: failed to parse -r argument: -2147483649
omnios$ date -r 2147483647
January 19, 2038 at 03:14:07 AM UTC
omnios$ date -r 2147483648
date: failed to parse -r argument: 2147483648
О, постойте-ка. Похоже, у OmniOS возникают проблемы с датами эпохи после 2^31. Интересно, что произойдёт, если мы не только отобразим дату, но и установим её? Давайте в цикле установим дату и выведем её:
omnios$ for s in 5 6 7 8; do sudo date -u 011903142038.0$s; date; done
January 19, 2038 at 03:14:05 AM UTC
January 19, 2038 at 03:14:05 AM UTC
January 19, 2038 at 03:14:06 AM UTC
January 19, 2038 at 03:14:06 AM UTC
January 19, 2038 at 03:14:07 AM UTC
December 13, 1901 at 08:45:52 PM UTC //!!!
ld.so.1: date: fatal: /lib/libc.so.1: Value too large for defined data type
Killed
omnios$ date
ld.so.1: date: fatal: /lib/libc.so.1: Value too large for defined data type
Killed
omnios$ ls
ld.so.1: ls: fatal: /lib/libc.so.1: Value too large for defined data type
Killed
omnios$ sudo reboot
sudo: unknown uid 100
sudo: error initializing audit plugin sudoers_audit
omnios$
Замечательно. Обратите внимание, что дата на самом деле оборачивается, но ОС сходит с ума. Но ведь у других систем не должно быть никаких проблем с установкой даты, правильно?
netbsd# date 197001010000; date +%s
Thu Jan 1 00:00:00 UTC 1970
0
netbsd# date 196912312359
date: settimeofday: Invalid argument
netbsd$
linux$ sudo date -s @0
date: cannot set date: Invalid argument
Thu Jan 1 12:00:00 AM UTC 1970
linux$ uptime; sudo date -s @7080
03:34:27 up 1:57, 1 user, load average: 0.04
Thu Jan 1 01:58:00 AM UTC 1970
Вот так, в NetBSD мы не можем установить дату до эпохи, а в Linux (с версии ядра 4.3) мы не можем установить дату до текущего аптайма.
settimeofday(2)
вернёт EINVAL
из-за гарантий, даваемых его "CLOCK_MONOTONIC
", который, согласно POSIX, "представляет количество времени после неуказанной точки в прошлом":Все варианты CLOCK_MONOTONIC гарантируют, что время, возвращаемое последовательными вызовами, не будет идти вспять, однако последовательные вызовы могут (в зависимости от архитектуры) возвращать идентичные (неувеличившиеся) значения времени.
С учётом всего этого, похоже, OmniOS вычисляет аптайм на основе времени запуска относительно системной даты, а, например, NetBSD и Linux хранят отдельные счётчики:
omnios$ sudo date -u 010100001970.00
January 1, 1970 at 12:00:00 AM GMT
omnios$ uptime
00:01:46 up -49 min(s), 1 user, load average: 0.00, 0.00, 0.02
omnios$ sudo date -u 011803142038.00
January 18, 2038 at 03:14:00 AM GMT
omnios$ uptime
03:14:11 up 5567 day(s), 7 min(s), 1 user, load average: 0.24, 0.16, 0.07
Попытка установить дату до эпохи тоже приводит к разным результатам. В NetBSD и Linux это сделать просто не удастся, а в OmniOS будет установлен месяц, день, час, минута и секунда, однако год будет ограничен значением 1970:
omnios$ sudo date -u 020100001969.00
February 1, 1970 at 12:00:00 AM GMT
omnios$ date
February 1, 1970 at 12:00:01 AM UTC
omnios$ sudo date -u 121308451901.52
December 13, 1970 at 08:45:52 AM GMT
omnios$ date
December 13, 1970 at 08:45:54 AM UTC
Но какую максимальную дату мы можем установить в системах, использующих 64-битный
time_t
? Как говорилось выше, можно ожидать, что доступно время до 2^63 - 1 = 9223372036854775807
эпохи. Давайте сначала отобразим дату, а затем попытаемся её установить:netbsd$ date -r 9223372036854775807
date: 9223372036854775807: localtime: Value too large to be stored in data type
netbsd$ date -r 67768036191676799
Wed Dec 31 23:59:59 UTC 2147485547
netbsd$ date -r 67768036191676800
date: 67768036191676800: localtime: Value too large to be stored in data type
linux$ date --date @9223372036854775807
date: time ‘9223372036854775807’ is out of range
linux$ date --date @67768036191676799
Wed Dec 31 11:59:59 PM UTC 2147485547
linux$ date --date @67768036191676800
date: time ‘67768036191676800’ is out of range
linux$
freebsd$ date -r 9223372036854775807
date: invalid time
freebsd$ date -r 67768036191676799
date: invalid time
freebsd$ date -r 67767976233532799
Tue Dec 31 23:59:59 UTC 2147483647
freebsd$ date -r 67767976233532800
date: invalid time
Это интересно. Несмотря на то, что нам обещали 64-битный
time_t
, мы не можем установить время на 9223372036854775807
. Похоже, максимальное значение равно 67768036191676799
в 2147485547 году. Хотя поначалу это значение кажется произвольным, можно заметить, что 2147485547 — это 2^31 - 1
, и внезапно всё обретает смысл: даже несмотря на то, что time_t
является 64-битным, tm_year
структуры struct tm
всё равно остаётся 32-битным, а потому максимальным значением, которое он может задать, является последняя секунда 2147485547 года.А как дела в FreeBSD? Почему в ней время
67768036191676799
эпохи является недопустимым, а 67767976233532799
эпохи соответствует тому, что на других платформах является 67768036191676799
? Если провести вычисления, то можно заметить, что разница между этими двумя моментами времени эпохи равна 1900 годам, то есть, очевидно, FreeBSD основывает свой tm_year
не на 1900 годе (как заявляется struct tm
в <time.h>
), а на 0 годе? Так как я не смог с этим разобраться, то отправил баг-репорт.Если мы попытаемся установить дату, то поначалу заметим, что при использовании
date(1)
NetBSD невозможно установить дату выше 9999 года (ещё один баг-репорт), но забавно, что на самом деле это не важно, поскольку мы всё равно не можем добраться выше 4147 года:netbsd# date 414708200732.17
date: settimeofday: Invalid argument
netbsd# date 414708200732.16; date +%s
Sun Aug 20 07:32:16 UTC 4147
68719476736
Так получилось потому, что в NetBSD есть жёстко прописанное ограничение в
2^36 = 68719476736
для значения tv_sec
, которое принимается при установке времени, потому что бОльшие значения вызывают недовольство KUBSAN (код): /*
* Установка завышенного значения времени
* приводит к недопустимому поведению системы.
*/
if (ts->tv_sec < 0 || ts->tv_sec > (1LL << 36))
return EINVAL;
В Linux используется другой практический максимум даты, установленный на 2232 год:
linux$ sudo date -s @8277292036
date: cannot set date: Invalid argument
Wed Apr 18 11:47:16 PM UTC 2232
linux$ sudo date -s '@8277292035'
Wed Apr 18 11:47:15 PM UTC 2232
linux$ sleep 10; date +%s
8277292045
linux$ sudo date -s @$(date +%s)
date: cannot set date: Invalid argument
Wed Apr 18 11:47:25 PM UTC 2232
Причина этого ограничения, найденная в исходном коде, заключается в том, что оно может использовать 30-летний аптайм до оборачивания счётчика:
#define NSEC_PER_SEC 1000000000L
#define KTIME_MAX ((s64)~((u64)1 << 63))
#define KTIME_SEC_MAX (KTIME_MAX / NSEC_PER_SEC)
/*
* Ограничения settimeofday():
*
* Чтобы предотвратить установку времени близко к точке оборачивания,
* устанавливаемое время ограничено так, чтобы можно было использовать разумный аптайм.
* Достаточно будет аптайма в 30 лет,
* то есть точкой отсечки является 2232. На этом этапе эта отсечка
* является просто небольшой частью более серьёзной проблемы *
*/
#define TIME_UPTIME_SEC_MAX (30LL * 365 * 24 *3600)
#define TIME_SETTOD_SEC_MAX (KTIME_SEC_MAX - TIME_UPTIME_SEC_MAX)
Это означает, что в Linux знаковое 64-битное максимальное значение времени (
2^63 - 1 = 9223372036854775807
) не обозначает секунды после эпохи, а подсчитывает наносекунды после эпохи, поэтому теоретическая максимальная дата Linux (KTIME_SEC_MAX
) снижается до всего лишь 9223372036
секунд эпохи, или даты 2262-04-23T11:47:16, что очень далеко от «22 приблизительных возрастов Вселенной», и ближе к тому, чтобы стать реальной проблемой.Проверяя, какое значение FreeBSD позволяет установить для часов, я выяснил, что в двух последних релизах это значение различается, но оба релиза имели неприятную проблему, приводящую к спонтанной перезагрузке системы:
freebsd# date -u -f "%s" 49282253052249598
Fri Dec 31 23:59:58 UTC 1561694399
49282253052249598
freebsd# sleep 1; date; date +%s
Fri Dec 31 23:59:59 UTC 1561694399
49282253052249599
freebsd# sleep 1; date; date +%s
Sat Jan 1 00:00:00 UTC 1561694400
49282253052249600
[ system reboots ]
Примечание: изначально я могу установить дате значение больше
49282253052249598
, но спустя примерно три секунды система перезагружается. Если задать дату на одну секунду меньше, то есть 49282253052249597
, то система не перезагружается, даже когда системное время уходит дальше следующего значения. Разве компьютеры — это не чудесно?О, и ещё кое-что...
Всем нам нравится, что macOS — это UNIX; она является одной из всего шести зарегистрированных на данный момент систем (остальные — это AIX, EulerOS (коммерческий дистрибутив Linux, созданный Huawei), HP-UX, Xinuos (ранее UnixWare, создан старыми AT&T Unix System Laboratories + Novell, SCO,
Caldera и UnXis) и z/OS). Однако фреймворк Core Foundation компании Apple не использует эпоху Unix как основу своего времени. В качестве его даты отсчёта используется 2001-01-01T00:00:00 GMT:
Все варианты CLOCK_MONOTONIC гарантируют, что время, возвращаемое Core
Foundation, измеряет время в секундах. В качестве базового типа данных используется CFTimeInterval, измеряющий разность в секундах между двумя моментами времени. Фиксированные моменты времени, или даты определяются типом данных CFAbsoluteTime, измеряющим интервал времени между конкретной датой и абсолютной датой отсчёта Jan 1 2001 00:00:00 GMT.
То есть если вы захотите преобразовать эти метки времени в эпоху Unix, нужно будет прибавить
978307200
.Как я и сказал, время — иллюзия, а время Unix — иллюзия вдвойне. И хотя вас может и не беспокоить проблема 2038 года (наверно, только если вы не пользуетесь OmniOS), возможно, мне удалось показать, что внутри может таиться множество других сюрпризов. А ведь мы ещё не касались безумия, творящегося с часовыми поясами и летним временем…
Комментарии (45)
Kremleb0t
28.10.2022 09:092016-12-31T23:59:60 is 1483228800
2017-01-01T00:00:00 is 1483228800 //здесь дублирующаяся метка времениЧто-то я не понял. Я не очень хорошо знаю си, но судя по исходнику https://www.netmeister.org/blog/epoch-time.c автор статьи преобразует строку "2016-12-31T23:59:60" в структуру tm а затем из структуры tm формирует unix timestamp. А чего он ожидал вообще? 2016-12-31T23:59:60 - такого времени не бывает. Автор сам подставил в аргумент функции mktime() дичь а потом жалуется на дублирующую метку времени. По хорошему, функция mktime() вообще должна отказываться работать когда в её аргумент подставляют невалидное значение.
Сама по себе корректировка времени - это костыль в несовершенных правилах счёта времени. Т.е. правила которые придумали люди для счёта времени расходятся с физическими явлениями (скоростью вращения Земли). Проблема не в unix-времени а в том, что сами правила счёта времени несовершенны.
M_AJ
28.10.2022 09:44+6такого времени не бывает.
Как раз бывает. Автор же там пишет про секунды коррекции, когда чтобы не сбить календарь нужно добавить в сутки дополнительную секунду. "Проблема" в том, что Unix писали не астрономы, и не учитывали такие вещи. Проблема в кавычках, потому что для обычных людей это не важно, а если тебе нужно хранить какие-нибудь астрономические данные, чувствительные к таким вещам, то просто не используй дефолтное Unix-время. Так что это просто любопытная техническая особенность.
Kremleb0t
28.10.2022 10:38+4Проблема" в том, что Unix писали не астрономы, и не учитывали такие вещи
Не считаю нужным учитывать все прихоти астрономов. Сегодня у них +1 секунда в году, а завтра они число секунд дробное придумают. Это всёравно что играть в шахматы с игроком, который находу придумывает свои правила.
Операционная система нужна чтобы решать конкретные практические задачи и она их решает. А то что кто-то захотел прибавить или убавить секунду в году - меня это не волнует, и я прекрасно понимаю разработчиков ОС которые за 50 лет так и не стали учитывать эту секунду.
Статья звучит как камень в разработчиков ОС, автор критикует метод расчёта unix timestamp, но не предлагает решения этой проблемы.
Iv38
28.10.2022 12:05+6Из-за секунды коррекции получается неправильное число секунд между двумя датами, а это может быть важно не только астрономам.
transcengopher
28.10.2022 13:11+2Операционная система нужна чтобы решать конкретные практические задачи и она их решает.
…
я прекрасно понимаю разработчиков ОС которые за 50 лет так и не стали учитывать эту секунду.Машиночитаемым вариантом передачи метки момента времени является передача Epoch Timestamp в виде числа — либо секунд, либо миллисекунд, я очень часто видел такие форматы на практике. Если в такую "консервативную" систему (не учитывающую коррекционные секунды) передавать метки даты в вышеупомянутом формате откуда-нибудь извне (где коррекционные секунды учитываются), то одна и та же метка после трансляции в человеко-читаемое время будет отображать как минимум разное количество секунд или минут на разных машинах.
А если вспомнить про ещё один иногда применяющийся паттерн, когда Epoch Timestamp передают и распознают как "время начала дня", и, получая Timestamp, извлекают из него только год, месяц, и день, то при передаче "метки начала дня" в такую систему, она после отбрасывания всех компонентов времени вообще может начать показывать другую дату.
Вы всё ещё считаете, что такие системы "решают конкретные практические задачи", и делают это правильно?Kremleb0t
28.10.2022 14:16Формат unix timestamp во многих местах очень удобен. Например, чтобы обозначить время +1 сутки достаточно к текущему времени прибавить 86400. И для сравнения/вычисления дат не нужны никакие объекты типа DateTime.
storoj
28.10.2022 14:41+7Так а что если как раз не хватит той одной секунды, чтобы попасть в следующий день?
rrrad
28.10.2022 18:09+3Чаще всего причиной возврата другой даты становится то, что преобразование производятся в разных часовых поясах. Иногда к этому примешивается то, что в одной из взаимодействующих систем забыли обновить таблицы временных зон, в этом случае ошибка возникает даже для двух систем, настроенных на вывод времени в одной временной зоне.
inkelyad
29.10.2022 13:41А если вспомнить про ещё один иногда применяющийся паттерн, когда Epoch Timestamp передают и распознают как "время начала дня",
Бить по рукам. Потому что оно создает проблемы даже без лишних секунд - а просто из за разных и/или ошибочных таймзон и их баз. Если тебе нужна именно дата - передавай дату.
Lissov
30.10.2022 12:34Не считаю нужным учитывать все прихоти астрономов. Сегодня у них +1 секунда в году, а завтра они число секунд дробное придумают.
Ну да, когда-то у них было 365 в каждом году, а у некоторых до сих пор 354. Это очень удобно для их практических задач определения фаз луны, да и разница времени между месяцами очень просто рассчитывается. А что времена года уезжают - это уже другая задача :)
То есть у нас есть два подхода:
Хранить год/месяц/день…/мс, точно и удобно отображать, но нетривиально рассчитать разницу между двумя моментами и нужна сложная структура данных (усложняются часы).
Хранить секунды или миллисекунды от заданного момента. Очень просто хранить и считать компьютеру, но нетривиально отображать.
TheProgger
28.10.2022 09:47+7Вообще говоря время 2016-12-31T23:59:60 Бывает
Сама по себе корректировка времени - это костыль в несовершенных правилах счёта времени. Т.е. правила которые придумали люди для счёта времени расходятся с физическими явлениями (скоростью вращения Земли)
А какие можно придумать правила, если скорость вращения Земли непостоянна и в общем случае непредсказуема?
unC0Rr
28.10.2022 09:39+11Не правила несовершенны, а скорость вращения Земли несовершенна. То быстрее крутится, то медленнее. Да ещё и количество оборотов вокруг собственной оси за один оборот вокруг солнца нецелое.
Naf2000
28.10.2022 09:39+6на западе, ... в Токио ... ; на востоке, в Осаке
так вот Токио восточнее Осаки
Fr0sT-Brutal
28.10.2022 10:15Просто оставлю здесь классическую байку про ракетные движки, железную дорогу и лошадиную задницу :) https://romx.livejournal.com/166141.html
Очень уж история происхождения unix эпохи напомнила
alexhott
28.10.2022 10:54+3А почему проблема именно для UNIX? В других системах есть корректировка на секунды в соответствии с расчетами службы времени?
В изначальной статье мягкое с теплым смешали, а тут еще корявый перевод добавился.screwer
28.10.2022 11:23+4В других системах время изначально 64 битное. И не в секундах, и в не наносекундах, а в 100-наносекундных интервалах. И не с 1900/1970/1971/2001 а с 1600 года. И в прошлое разумный отступ, и интервал способен учитывать размерности менее секунд, и в будущее огромный запас. И это все работает сразу, с самого первого релиза.
vassabi
28.10.2022 11:36+3ну, теперь-то легко не повторять ошибок прошлого :)
screwer
28.10.2022 12:46+1"Ошибку прошлого" совершили в 1970, повторили в 1986-1996 (GNU, Linux, и т.д.)
А "не совершили" в 1988-1993, используя как основу систему из второй половины 1970х (к сожалению не знаю, как там время устроено).
Как мы видим, временые периоды почти одинаковы.
vassabi
28.10.2022 12:56-1ну, самая большая лигаси ИМХО - это попытка постоянно представлять время в виде одного числа одной и той же размерности.
Гораздо лучше ему бы было в виде массива байт: чтобы биты первого байта задавали длину остальных (см. например как UNICODE), а остальные уже хранили число (наносекунд, секунд или 1/100 наносекунд и т.д.)
rrrad
28.10.2022 18:15+4Такое допустимо для передачи значений, но любая обработка такого значения превратится в ад с массой проверкой и кучей особых случаев. Обрабатывать гораздо проще значения с фиксированным форматом.
slonopotamus
28.10.2022 20:47+2И это все работает сразу, с самого первого релиза
Ну в софте от микрософта тоже есть всякие приколы с датами: https://learn.microsoft.com/en-us/office/troubleshoot/excel/wrongly-assumes-1900-is-leap-year
gsaw
28.10.2022 11:33+2Мне вот непонятно, разве нельзя предсказать на годы вперед где окажется земля в тот или иной момент времени? Можно же было бы тогда расчитать все восходы и закаты на годы вперед. Тогда не пришлось бы делать коррекцию постфактум, а просто перещелкивать дни по табличным значениям. Или тут есть какая то доля непредсказуемости из-за влияния других планет?
TheMrWhite
28.10.2022 11:36+8задача трёх тел
gsaw
28.10.2022 11:41+1Ну это понятно, просто на каком отрезке времени доля хаоса становится больше секунды, миллисекунды? Ну то есть к примеру если на отрезок лет 50 можно рассчитать, даже с учётом хаоса, то потом можно посчитать по новой, лет чрез 50, 20, 10.
vassabi
28.10.2022 11:45+4если вам для "ну будет минутой больше или меньше - и так сойдет", то можно наверно и на 100 лет вперед рассчитать
domix32
28.10.2022 17:41+3Для такого необходимо знать все гравитационные силы воздействующие на тело, а учитывая, что огромная часть массы скрыта пересчет думается происходит постфактум, когда предсказанная и фактическая разница измерений случается.
0xd34df00d
28.10.2022 22:25+7Проблема не только в (точечных) телах, но и в том, что на скорость вращения Земли влияет, например, и ветер на этой самой Земле, и другие подобные явления.
vassabi
28.10.2022 11:43+11) а как вы думаете - с какой точностью делают измерения положения и скорости Земли на орбите? А также положения и скорости других небесных тел, влияющих на Землю ? (полмиллиметра на миллион тонн массы или меньше? или больше ?)
2) какие у вас критерии - что влияет на траекторию Земли, а что - не влияет ? (пояс астероидов и Меркурий - влияют ? или только Луна и Солнце ?)
3) и, как вы думаете - как быстро накапливается расхождение фактического положения от предрассчитанного со временем ? (на микроградусы в год или больше ? или меньше ?)
gsaw
28.10.2022 11:44+2не знаю, потому и спрашиваю.
vassabi
28.10.2022 11:50+3ну просто если "рассветы и закаты - для людей", то там рассчитывают очень примерно (ибо еще и высота над горизонтом у всех будет разная), и там можно надолго вперед рассчитать.
А если вы - астроном и вам нужно прицелиться на нужную звезду с поверхности Земли, то там увы одна секунда ухода в несколько лет - это именно то, что "не могут рассчитать заранее" (а иначе - просто рассчитывали бы и в ус не дули)
victor_1212
28.10.2022 16:06> а как вы думаете - с какой точностью делают измерения положения и скорости Земли на орбите
если Вас интересует годичный параллакс, то он незначителен max порядка долей угловой секунды, т.е в большинстве приложений можно считать что звезды на бесконечности, в первом приближении имеет значение только вращение земли
> А если вы - астроном и вам нужно прицелиться на нужную звезду с поверхности Земли
чтобы точно прицелиться на нужную звезду зная ее угловые координаты желательно иметь стабильную монтировку, и соответствующие средства управления (включая привода, угловые датчики, контроль механических деформаций и пр. ) в общем случае точность такого прицеливания на 2-3 порядка хуже точности знания координат звезд по каталогу, что тем не менее достаточно для попадания в поле зрения, но для точного наведения обычно используется относительный метод с распознаванием звездного узора
saege5b
28.10.2022 13:18+2Сначала нужно составить объёмную точную гравиметрическую карту Земли - распределения масс в объёме; а потом повторить этот фокус для Луны и Солнца.
Для горизонта лет в пару хватит, а там можно и окружение притягивать.
Только, пока гравиметрия Земли оперирует блоками в сотни и тысячи километров, отсутствует какая либо адекватная модель происходящего внутри планеты (ядро плюс мантия), и да, прогнозы строятся и до недавнего времени они даже примерно попадали. :)
engine9
28.10.2022 14:13+2Примерно рассчитывали на десятилетия вперёд еще до нашей эры. Даже делали механические компьютеры для рассчета фаз Луны, затмений, положения планет на небе и т.п.
Apoheliy
29.10.2022 00:49По мне всё проще: Лень и Зачем?!
Зачем на годы вперёд всё подсчитывать? Если вы знаете (например), что коррекция будет через 173 года ... то что?
Ок, слишком далеко. Пусть коррекция будет через 2 года ... и ... что? Броситесь переписывать использование функций времени с учётом коррекции? Добавлять/убавлять? А если функции уже будут с учётом коррекции (ОЧЕНЬ вероятное событие)?
Если в одном календаре (например, григорианский) коррекция есть, а в другом (юлианский, иранский, еврейский, ...) коррекции нет, ... то ... что?
rezedent12
28.10.2022 13:02+1Может не надо дёргать время UNIX вслед за астрономическим? Пусть идёт себе равномерно. А для конвертаций в воспринимаемый человеком вид, использовать журнал с количеством секунд в каждом году.
saege5b
28.10.2022 13:19+2Надо как-то этот журнал по всем системам рассовывать постфактум.
13werwolf13
28.10.2022 13:27+2в эпоху повсеместного распространения интернета и операционок которые обновляются не спрося у пользователя разрешения это не большая проблема. но в целом я согласен что идея фиговая и очень костыльная. да и реализация породит 100500 несовместимых форматов этого журнала и кучу конвертеров с багами и артефактами.
mbalabin
28.10.2022 14:33+5Вращение Земли ускоряется и замедляется непредсказуемо. Если вы хотите согласовывать значение аппаратных часов с астрономическим временем, то у вас нет другого варианта кроме как рассовывать журнал постфактум. Заранее узнать об изменении скорости вращения невозможно - часы придётся тем или иным способом "подводить". Кроме того, постоянно меняются часовые пояса и правила перехода с летнего времени на зимнее. Эту информацию тоже приходится как-то доводить до конечных систем. Так что решение не двигать UNIX-время очень разумное. Кстати, точно так же не двигается GPS-время и TAI-время.
Apoheliy
28.10.2022 14:16+3Вот что происходит, когда люди начинают частности натягивать на "вообще".
Смотрим на POSIX: https://pubs.opengroup.org/onlinepubs/9699919799/functions/time.html
там сказано про секунды от "эпохи". Указание на 01.01.1970 есть? Нет! Т.е. условная линукса может считать от любой даты!!! Весело?
Смотрим на C++:
https://en.cppreference.com/w/c/chrono/time
формат представления не специфицирован (хотя обычно совпадает с POSIX) - т.е. это могут быть хоть 1/60 секунды, хоть минуты. Весело?
По факту возвращаемое время может быть в любых интервальных единицах от любой базовой даты.
Учите МатЧасть, читайте стандарт.
vassabi
28.10.2022 15:21извините, а это не вы автор проигрывания музыки и показывания веселых картинок, когда программа на С++ натыкается на UB ?
M_AJ
Качество перевода конечно огонь. Даже переводчик Гугла уже таких ошибок не делает.
PatientZero Автор
Спасибо, исправил.
vdudouyt
Не знал, что Осака теперь находится к востоку от Токио :)