стенд стоял на неявном умолчании: колонки времени объявлены без пояса, и
сходилось всё лишь потому, что пояс сервера — UTC. Правило нужно принять
до того, как написаны dds.* и витрины: там оно и понадобится — воронки,
удержание, «покупки по дням».
Что:
конвенция записана в docs/architecture/storage.md, раздел «Часовые
пояса»: линза называется явно — в типе колонки либо в вызове функции;
какая именно, решает слой по тому, кого он обслуживает.
ODS говорит на языке выгрузки: UTCEventTime — DateTime('UTC'),
служебные метки _load_ts и kafka_timestamp — DateTime64(3, 'UTC')
в обоих слоях.
parseDateTimeOrNull получил третьим аргументом 'UTC': суффикс Z
маска сверяет как букву и выбрасывает, поэтому тип колонки путь записи
не чинит.
контракт схемы, «описание выгрузки» и таблица колонок мастер-спеки
несут тип с поясом; имя Europe/Samara стоит комментарием к COUNTER_TIMEZONE_MINUTES.
учебный комментарий про пояс — у первой колонки, где он назван.
Проверка:
make lint, make typecheck, make test (407)
make clean && make up && make check-clickhouse — 9 из 9
make smoke — 20, make check-services — 7
замер тикета повторён: под session_timezone='Europe/Samara' колонка
показана 2026-05-31 23:37:00, как и без настроек — глаз и GROUP BY
перестали спорить
Разбор двух холодных ревью и снятые замеры — комментарием в #63.
- Зачем:
- стенд стоял на неявном умолчании: колонки времени объявлены без пояса, и
сходилось всё лишь потому, что пояс сервера — UTC. Правило нужно принять
до того, как написаны `dds.*` и витрины: там оно и понадобится — воронки,
удержание, «покупки по дням».
- Что:
- конвенция записана в `docs/architecture/storage.md`, раздел «Часовые
пояса»: линза называется явно — в типе колонки либо в вызове функции;
какая именно, решает слой по тому, кого он обслуживает.
- ODS говорит на языке выгрузки: `UTCEventTime` — `DateTime('UTC')`,
служебные метки `_load_ts` и `kafka_timestamp` — `DateTime64(3, 'UTC')`
в обоих слоях.
- `parseDateTimeOrNull` получил третьим аргументом `'UTC'`: суффикс `Z`
маска сверяет как букву и выбрасывает, поэтому тип колонки путь записи
не чинит.
- контракт схемы, «описание выгрузки» и таблица колонок мастер-спеки
несут тип с поясом; имя `Europe/Samara` стоит комментарием к
`COUNTER_TIMEZONE_MINUTES`.
- учебный комментарий про пояс — у первой колонки, где он назван.
- Проверка:
- `make lint`, `make typecheck`, `make test` (407)
- `make clean && make up && make check-clickhouse` — 9 из 9
- `make smoke` — 20, `make check-services` — 7
- замер тикета повторён: под `session_timezone='Europe/Samara'` колонка
показана `2026-05-31 23:37:00`, как и без настроек — глаз и `GROUP BY`
перестали спорить
Разбор двух холодных ревью и снятые замеры — комментарием в #63.
Closes #63
- Зачем:
- пояс в стенде нигде не назван: числа верны только потому, что сервер
ClickHouse стоит в UTC, а правило понадобится в dds и витринах —
воронки, удержание, «покупки по дням» (#63).
- Что:
- раздел «Часовые пояса»: пояс — линза, называется в типе колонки либо в
вызове; какая именно — решает слой (ODS на языке выгрузки, DDS и витрины
на языке бизнеса); день берётся из EventDate.
- названы оба перехода, где пояс выбирается, включая разбор строки в
матвью — он берёт пояс у сервера и тип колонки этого не чинит.
- отвергнутые варианты прозой: умолчание сервера, TZ серверу, ODS в поясе
счётчика, хранение местного времени.
- три замера ушли в «Что проверено», сверка с документацией — от 8 августа.
- Проверка:
- make lint
- DDL к конвенции ещё не приведён: документ описывает цель, код идёт
следом тем же тикетом.
- Зачем:
- раздел «Часовые пояса» прошёл два холодных ревью — по дефектам и по
уместности. Первое поймало ложный замер и три расхождения с живым
стендом, второе — материал не своей зоны и дубли (#63).
- Что:
- замер «расхождение живёт по HTTP» отозван: мерил toString(UTCEventTime)
в родном клиенте против голой колонки по HTTP, а это разные вещи.
Перемерено — клиенты ведут себя одинаково; записан верный факт: вывод
колонки идёт по поясу сессии, функция — по поясу типа.
- «по поясу сервера» заменено на «по поясу сессии, а тот по умолчанию
серверный» — в разделе и в ADR 0005; утверждение в ледгере переписано
под измеренный раскол вывода и типа.
- PARTITION BY toDate(_load_ts) больше не выдаётся за уже соблюдённое
правило: _load_ts сегодня DateTime64(3) без пояса.
- абзац ADR 0005 больше не спорит с цитатой вызова строкой выше.
- вырезано: веер отклонённых вариантов под заголовком (живые отказы
разложены прозой по своим абзацам, как принято в этом документе),
ссылка на несуществующую связку в world.py, осиротевшая строка про
Grafana, абзац про пояс показа — он уехал комментарием в #63.
- Проверка:
- make lint
- замеры повторены на живом стенде 8 августа 2026 года
- DDL к конвенции по-прежнему не приведён: документы описывают цель
- Зачем:
- конвенция #63 записана, а код её не достиг: колонки времени стояли без
пояса, и сходилось всё лишь потому, что пояс сервера — UTC.
- Что:
- UTCEventTime объявлен DateTime('UTC'), служебные метки _load_ts и
kafka_timestamp — DateTime64(3, 'UTC') в STG и ODS.
- parseDateTimeOrNull получил третьим аргументом 'UTC': маска сверяет
суффикс Z как букву, зоны из строки не берёт вовсе.
- контракт схемы и описание выгрузки несут тип с поясом; имя пояса
Europe/Samara встало рядом со смещением в world.py, сходимость сверяет
тест.
- учебный комментарий о линзе — у первой колонки с явным поясом.
- Проверка:
- make lint, make typecheck, make test (408 тестов)
- make clean && make up && make check-clickhouse — 9 из 9
- замер тикета повторён: под session_timezone='Europe/Samara' колонка
показана 2026-05-31 23:37:00, как и без настроек
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Зачем:
- линия дефектов нашла три неверных утверждения и мёртвый замер, линия
уместности — три пересказа уже сказанного.
- Что:
- «тип колонки не решает, какое число ляжет» сужено до правды: разбор
отдаёт готовое число, а пояс приёмника решал бы судьбу строки.
- замер до правки типов помечен как неповторяемый на нынешнем стенде.
- правило о поясе сервера привязано к местам, где линза что-то решает:
матвью приёма пояс не называет, и это не нарушение.
- убраны: пересказ механики в ADR 0005, четыре строки учебного
комментария, утверждение о порядке файлов и «секунды от начала эпохи»
у миллисекундной метки.
- Проверка:
- make lint, make typecheck, make test (408 тестов)
- make clean && make up && make check-clickhouse — 9 из 9
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Зачем:
- константу COUNTER_TIMEZONE не читал ни один модуль, единственным её
читателем был тест про неё же; связь имени и смещения держится тем, что
они стоят в одной строке.
- Что:
- COUNTER_TIMEZONE снят, имя пояса ушло комментарием к
COUNTER_TIMEZONE_MINUTES.
- тест сходимости имени и смещения снят вместе с ним; test_world.py
вернулся к прежнему виду.
- Проверка:
- make lint, make typecheck, make test (407 тестов)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Зачем:
- фраза «Date не участвует вовсе» выводила тип из-под общего правила:
про объявление это правда, про употребление — нет. Считая время по
EventDate без имени пояса, легко получить часы вне диапазона.
- Что:
- фраза заменена на две: Date хранит только номер дня, пояс при счёте
времени называют руками, промах виден по часам за границами 0–23.
- Проверка:
- замерено на стенде: без имени пояса часы от начала суток идут -4…19,
отрицательных 15 843 события; с названным поясом — 0…23
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ddmitry
merged commit 5d012cc222 into main2026-08-08 20:16:57 +03:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
сходилось всё лишь потому, что пояс сервера — UTC. Правило нужно принять
до того, как написаны
dds.*и витрины: там оно и понадобится — воронки,удержание, «покупки по дням».
docs/architecture/storage.md, раздел «Часовыепояса»: линза называется явно — в типе колонки либо в вызове функции;
какая именно, решает слой по тому, кого он обслуживает.
UTCEventTime—DateTime('UTC'),служебные метки
_load_tsиkafka_timestamp—DateTime64(3, 'UTC')в обоих слоях.
parseDateTimeOrNullполучил третьим аргументом'UTC': суффиксZмаска сверяет как букву и выбрасывает, поэтому тип колонки путь записи
не чинит.
несут тип с поясом; имя
Europe/Samaraстоит комментарием кCOUNTER_TIMEZONE_MINUTES.make lint,make typecheck,make test(407)make clean && make up && make check-clickhouse— 9 из 9make smoke— 20,make check-services— 7session_timezone='Europe/Samara'колонкапоказана
2026-05-31 23:37:00, как и без настроек — глаз иGROUP BYперестали спорить
Разбор двух холодных ревью и снятые замеры — комментарием в #63.
Closes #63
- Зачем: - пояс в стенде нигде не назван: числа верны только потому, что сервер ClickHouse стоит в UTC, а правило понадобится в dds и витринах — воронки, удержание, «покупки по дням» (#63). - Что: - раздел «Часовые пояса»: пояс — линза, называется в типе колонки либо в вызове; какая именно — решает слой (ODS на языке выгрузки, DDS и витрины на языке бизнеса); день берётся из EventDate. - названы оба перехода, где пояс выбирается, включая разбор строки в матвью — он берёт пояс у сервера и тип колонки этого не чинит. - отвергнутые варианты прозой: умолчание сервера, TZ серверу, ODS в поясе счётчика, хранение местного времени. - три замера ушли в «Что проверено», сверка с документацией — от 8 августа. - Проверка: - make lint - DDL к конвенции ещё не приведён: документ описывает цель, код идёт следом тем же тикетом.- Зачем: - раздел «Часовые пояса» прошёл два холодных ревью — по дефектам и по уместности. Первое поймало ложный замер и три расхождения с живым стендом, второе — материал не своей зоны и дубли (#63). - Что: - замер «расхождение живёт по HTTP» отозван: мерил toString(UTCEventTime) в родном клиенте против голой колонки по HTTP, а это разные вещи. Перемерено — клиенты ведут себя одинаково; записан верный факт: вывод колонки идёт по поясу сессии, функция — по поясу типа. - «по поясу сервера» заменено на «по поясу сессии, а тот по умолчанию серверный» — в разделе и в ADR 0005; утверждение в ледгере переписано под измеренный раскол вывода и типа. - PARTITION BY toDate(_load_ts) больше не выдаётся за уже соблюдённое правило: _load_ts сегодня DateTime64(3) без пояса. - абзац ADR 0005 больше не спорит с цитатой вызова строкой выше. - вырезано: веер отклонённых вариантов под заголовком (живые отказы разложены прозой по своим абзацам, как принято в этом документе), ссылка на несуществующую связку в world.py, осиротевшая строка про Grafana, абзац про пояс показа — он уехал комментарием в #63. - Проверка: - make lint - замеры повторены на живом стенде 8 августа 2026 года - DDL к конвенции по-прежнему не приведён: документы описывают цель- Зачем: - линия дефектов нашла три неверных утверждения и мёртвый замер, линия уместности — три пересказа уже сказанного. - Что: - «тип колонки не решает, какое число ляжет» сужено до правды: разбор отдаёт готовое число, а пояс приёмника решал бы судьбу строки. - замер до правки типов помечен как неповторяемый на нынешнем стенде. - правило о поясе сервера привязано к местам, где линза что-то решает: матвью приёма пояс не называет, и это не нарушение. - убраны: пересказ механики в ADR 0005, четыре строки учебного комментария, утверждение о порядке файлов и «секунды от начала эпохи» у миллисекундной метки. - Проверка: - make lint, make typecheck, make test (408 тестов) - make clean && make up && make check-clickhouse — 9 из 9 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>- Зачем: - константу COUNTER_TIMEZONE не читал ни один модуль, единственным её читателем был тест про неё же; связь имени и смещения держится тем, что они стоят в одной строке. - Что: - COUNTER_TIMEZONE снят, имя пояса ушло комментарием к COUNTER_TIMEZONE_MINUTES. - тест сходимости имени и смещения снят вместе с ним; test_world.py вернулся к прежнему виду. - Проверка: - make lint, make typecheck, make test (407 тестов) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>- Зачем: - фраза «Date не участвует вовсе» выводила тип из-под общего правила: про объявление это правда, про употребление — нет. Считая время по EventDate без имени пояса, легко получить часы вне диапазона. - Что: - фраза заменена на две: Date хранит только номер дня, пояс при счёте времени называют руками, промах виден по часам за границами 0–23. - Проверка: - замерено на стенде: без имени пояса часы от начала суток идут -4…19, отрицательных 15 843 события; с названным поясом — 0…23 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>