На мінулым тыдні я выклаў пост пра прагулку з сабакам. У ім былі 2 GPS-трэкі. Перад публікацыяй я зрушыў усе каардынаты ў Ціхі акіян: форма прагулкі застаецца, месца прападае. Мне здавалася, што гэтым доўг па тэме прыватнасці закрыты.
А потым я заўважыў, што пушу каміты ў публічныя рэпазіторыі з 2012 года, нічога ў іх не зрушваючы і ні разу не паглядзеўшы, што яны расказваюць. Пра тое, што часавы пояс чалавека адгадваецца па часе публічных камітаў, пішуць як пра простую справу. Я гэта прыняў на веру. Паказвалі мне гэта толькі на людзях, якіх ужо знайшлі.
Таму я прагнаў прыём на сабе. 26 маіх публічных рэпазіторыяў, не форкі, скланаваныя голымі з --filter=blob:none: цягне граф камітаў, змест файлаў прапускае. У клонах 675 камітаў, 574 мае, з іх 510 датаваныя да сённяшняга дня па UTC. git 2.43.0, gh 2.45.0, Python 3.12.3, Ubuntu 24.04.
Першы прагон знайшоў на 23 меней. Я фільтраваў па радку dimhold, а ў 23 камітах стаіць працоўны адрас былога наймальніка, якім я карыстаўся з красавіка 2015 да чэрвеня 2021. Гэты адрас называе кампанію.
26 клонаў, якія былі непатрэбныя
Эндпойнт commits у REST API нармалізуе даты аўтара ў Z. Каміт 8c290e4 прыходзіць як 2026-08-19T14:18:38Z. Дакладна так яго аддае і /git/commits/{sha}, гэта значыць эндпойнт, ад якога я чакаў сырых даных. Аб’ект каміта ў клоне кажа больш:
author Dmitriy Semenkevich <dimhold@gmail.com> 1787149118 +0300
Секунда unix і зрух ад UTC. Па мясцовым часе гэта 17:18:38. Далей усё будзе пра гэты зрух. Дзеля яго я і скланаваў 26 рэпазіторыяў. Не трэба было. GitHub аддае любы каміт патчам, а ў патчы стаіць паштовы загаловак:
$ curl -s https://github.com/dimhold/whotop/commit/8c290e4.patch | sed -n 3p
Date: Wed, 19 Aug 2026 17:18:38 +0300
Ні токена, ні клона. Ёсць і чацвёрты шлях, прычым дакументаваны. /stats/punch_card лічыць каміты па днях тыдня і гадзінах. У дакументацыі напісана, што гадзіны там “based on the time zone of individual commits”. Без лагіна ён аддае па той серадзе ячэйку [3, 17, 3], гэта значыць мясцовыя 17:00, а не 14:00 па UTC. Па ўсіх 26 рэпазіторыях гадзіны з punch_card сыходзяцца з мясцовымі гадзінамі з клонаў, а не з гадзінамі UTC. Рознасць 2 эндпойнтаў па ўсіх 26 дае +3, за 52 запыты і без клона. Па адным рэпазіторыі выходзіць слабей: +3 перамагае ў 18 з 26, а +2 у 6. Выявіў я ўсё гэта, калі 26 голых клонаў ужо ляжалі на дыску.
Ацэнка і 2 гадзіны, на якія яна прамахваецца
Прыём просты. Узяць публічныя меркі часу камітаў у UTC і перабраць усе 27 цэлагадзінных зрухаў. Пакінуць той, пры якім найменш камітаў трапляе ў гадзіны, калі дарослы чалавек, як мяркуецца, спіць. Акно я ўзяў з 01:00 да 06:59 па мясцовым часе.
На 496 камітах, якія аддаў REST API, перамагае адзін зрух, прычым упэўнена. Гэта +1, пры 6 камітах унутры акна сну. У 422 аб’ектах маіх камітаў з 510 запісана +03:00, гэта значыць +3 гэта мода, а не канстанта. Пры +3 тыя ж 496 даюць у акне 28.
Значыць, ацэнка прамахваецца на 2 гадзіны. Зруху +01:00 няма ні ў адным з маіх 510 камітаў, так што прыём вяртае пояс, з якога я не камітаваў ніколі.
Чаму прамахваецца
Я працую пасля поўначы. 32 мае каміты стаяць у мясцовых гадзінах 00 і 01. У акне, якое прыём лічыць сном, ляжаць 28 камітаў. Па 510 камітах з клонаў выходзіць той жа лік, што па 496 з REST. Пры той жа шырыні ў 6 гадзін найцішэйшае маё акно пачынаецца а 03:00 і трымае 12. 2 вокны разыходзяцца на 2 гадзіны. Гэта памер памылкі.
Ацэнка знаходзіць той зрух, пры якім я падобны да чалавека, які нармальна спіць. Да гэтага зруху яна прыходзіць так: адсоўвае мой гадзіннік назад, пакуль праца пасля поўначы не пачне чытацца як позні вечар. Гэтую версію я не праверыў: не выкідаў тыя каміты і не пералічваў ацэнку нанова.
Я чакаў, што больш даных вылечыць прамах. Выходзіць наадварот. Я 2000 разоў браў выпадковую падвыбарку з 496 гадзін UTC на кожны яе памер. Пры n=20 ацэнка кажа +1 у 41.2% прагонаў. Пры n=100 яна кажа +1 у 59.5%, пры n=200 у 67.3%. Сапраўднае +3 на тым жа інтэрвале падае з 9.8% да 0.1%. Падвыбаркі паказваюць, што ацэнка сыходзіцца да раскладу аднаго чалавека, таму зламанае дапушчэнне ад нетыповага рэжыму сну яна адрозніць не можа.
Хто гэта ўжо зрабіў
Само поле зруху добра стаптанае. Праца на OpenSym у 2016 годзе чытала яго з логаў git і з загалоўкаў рассылак. Праца на SWAN у 2017 гнала па ім кругавую статыстыку. Праца на MSR у 2022 дапамагла разнесці па карце 2.2 мільярда камітаў. Mo Beigi апублікаваў прыём з патчам у траўні 2025 і аднавіў па ім паездку калегі. Стаптанае канчаецца там, дзе пачынаецца ацэнка. У абмеркаваннях бэкдора ў xz у красавіку 2024 адзін аўтар палічыў зрухі і атрымаў +0800. Другі паглядзеў на гістаграму гадзін і напісаў, што гіпотэза UTC+03 “seems to have been backwards”. Тыя ж каміты, 2 дасведчаныя чалавекі, 2 адказы. Зверыць іх было не з чым.
Што зрух апісвае насамрэч
У 7 маіх камітах стаіць -07:00 і -08:00, усе датаваныя паміж 2012 і 2014 гадамі. Я не разумеў, адкуль яны. Усе 7 сыходзяцца з America/Los_Angeles, а між першым і апошнім умяшчаюцца 4 пераходы на летні час. Адзін з іх стаіць на -08:00 за цэлыя 4 дні да пераходу таго года. Гадзіннік, які ніхто не наладжваў, не адсочвае амерыканскі пераход на летні час 21 месяц падрад.
Потым я паглядзеў, што гэта за каміты. 6 з 7 гэта Initial commit без бацькі. Кожны праштампаваны ў межах 1 секунды ад часу стварэння самога рэпазіторыя. Сёмы гэта мерж пул-рэквеста нумар 1. Значыць 6 з 7 выйшлі з сервераў GitHub, а не з якой-небудзь маёй машыны, а пра сёмы я раблю выснову па адным паведамленні. Чый гадзіннік стаяў на амерыканскім ціхаакіянскім поясе, замер не кажа, ён кажа толькі, што ўсе 7 гэтаму поясу ідуць следам.
Працуе гэта і ў зваротны бок. 96 маіх камітаў з 510 стаяць на роўнай хвіліне. Гэта 18.8% там, дзе клавіятура дае 1 да 60. У гэтую групу трапляюць 65 камітаў з 66 са зрухам +02:00, а 41 мерка з 96 стаіць роўна на гадзіне. Амаль усе яны пастаўленыя рукамі або скрыптам, гэта значыць пятая частка таго, што я называю ісцінай, не пра час працы.
Чырвоная лінія
У клонах ляжалі не толькі мае каміты. 101 радок з 675 належаў 5 іншым адрасам. У іх аб’ектах зрух запісаны ў тым жа полі, што і ў маіх.
Гэта быў роўна той замер, якога артыкулу не хапала. Іншыя людзі, адказы вядомыя заўчасна і спосаб адрозніць зламанае дапушчэнне ад нетыповага рэжыму сну. Замест гэтага я выдаліў радкі разам з клонамі. Лік выдаленых радкоў пайшоў у others-count.txt без адрасоў.
Адмова абышлася танней, чым я думаў. Адзін з 5 адрасоў гэта бот з 3 камітамі. У 4 людзей 57, 34, 5 і 2. Ацэнка па 3 мерках часу трапляе ў +3 у 12.2% гэтых падвыбарак. Каліброўка мела сэнс толькі на 2 з 4. Мой галоўны рэзультат па-ранейшаму вымераны на выбарцы з аднаго чалавека.
Гэтыя меркі часу ляжаць у маім працоўным каталогу толькі таму, што я скланаваў рэпазіторый, у які іх уладальнікі калісьці камітавалі. Адзін радок awk разабраў бы іх па гадзінах. Тое, што гэта проста, я аргументам не лічу.
Чаго я не праверыў
Сетка кандыдатаў цэлагадзінная, таму +05:30 і +05:45 яна выразіць не ўмее. Хтосьці з іншых людзей у тых жа клонах камітаваў са зрухам -04:30. GraphQL API я не глядзеў. І я не праверыў, ці перапісваў рэбэйз даты камітаў 2026 года. Калі перапісваў, то зрухі, якія я бяру за ісціну па гэтым годзе, маладзейшыя за самі каміты.