Some checks failed
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 1m32s
Deploy Trade-In / deploy (push) Has been cancelled
Deploy Trade-In / changes (push) Successful in 11s
Deploy Trade-In / build-backend (push) Successful in 59s
79 lines
4.8 KiB
Python
79 lines
4.8 KiB
Python
"""avito_shared.py — общие константы/хелперы для avito*.py модулей.
|
||
|
||
Единая точка правды для:
|
||
- `RUS_MONTHS` — рус. месяц (родительный падеж) → номер, для парсинга дат вида
|
||
"9 октября 2025" (было продублировано в avito.py/avito_detail.py/avito_houses.py).
|
||
- `_unix_to_date` — unix-epoch (в СЕКУНДАХ) → `date`, в таймзоне Europe/Moscow.
|
||
|
||
TZ-обоснование (issue #2129): было 3 независимые копии конвертации unix→date с
|
||
РАЗНЫМИ timezone — MSK (avito.py, документировано issue #726), UTC (avito_houses.py),
|
||
naive/system-local (avito_imv.py). Один и тот же epoch давал разные `date` в
|
||
SERP / houses / IMV путях для одного и того же объявления.
|
||
|
||
Почему MSK — канонический выбор (не UTC, не local):
|
||
- Avito — российская площадка, отображает даты объявлений (`item-date` на SERP,
|
||
`publishDate`, история цен `startPriceDate`/`lastPriceDate`/`removedDate`) в
|
||
Europe/Moscow. Объявление, поднятое в 00:00–03:00 MSK, при наивной UTC-конвертации
|
||
получило бы дату на день раньше, чем видно на самом Avito (обоснование #726).
|
||
- avito_houses.py и avito_imv.py используются на одних и тех же данных
|
||
(`housePlacementHistory` widget / IMV `placementHistory` — идентичные поля
|
||
startPriceDate/lastPriceDate/removedDate, см. fixtures/avito_imv_getdata.json) —
|
||
единственный смысл держать их согласованными с уже принятым MSK-решением из #726.
|
||
- Проверено на живой fixture: startPriceDate/lastPriceDate — всегда полночь UTC
|
||
(00:00:00), поэтому MSK vs UTC не меняет date для них. `removedDate`/`publishDate`
|
||
несут реальное время суток — там UTC/naive-local и MSK ДЕЙСТВИТЕЛЬНО расходятся
|
||
(пример из fixture: removedDate=1756077885 → UTC 2025-08-24, MSK 2025-08-25).
|
||
Это единственный реальный сдвиг данных от этого фикса: `avito_imv_evaluations` /
|
||
`house_placement_history`, поля `removed_date`/`publish_date`, у ~12.5% строк
|
||
(события в 21:00–23:59 UTC/local) дата сдвинется на +1 день при следующем прогоне
|
||
скрейпера. Схему/уже сохранённые строки не трогаем — это единственно правильное
|
||
представление на будущее.
|
||
- Fixed-offset `timezone(timedelta(hours=3))`, а не `zoneinfo("Europe/Moscow")` —
|
||
не зависит от системной tzdata в контейнере; Москва не переходит на DST с 2014,
|
||
так что fixed offset корректен всегда.
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import logging
|
||
from datetime import date, datetime, timedelta, timezone
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
# Europe/Moscow, UTC+3, без перехода на летнее время (с 2014).
|
||
MSK = timezone(timedelta(hours=3))
|
||
|
||
# Русские месяцы (родительный падеж) для парсинга дат вида "9 октября 2025".
|
||
RUS_MONTHS: dict[str, int] = {
|
||
"января": 1,
|
||
"февраля": 2,
|
||
"марта": 3,
|
||
"апреля": 4,
|
||
"мая": 5,
|
||
"июня": 6,
|
||
"июля": 7,
|
||
"августа": 8,
|
||
"сентября": 9,
|
||
"октября": 10,
|
||
"ноября": 11,
|
||
"декабря": 12,
|
||
}
|
||
|
||
|
||
def _unix_to_date(ts: int | float | None) -> date | None:
|
||
"""Конвертирует unix-epoch (в СЕКУНДАХ) в `date`, в таймзоне Europe/Moscow (MSK).
|
||
|
||
Возвращает `None` для `None`/`0` (Avito использует 0 как sentinel «нет даты») и
|
||
для значений, которые не конвертируются в валидную дату.
|
||
|
||
ВАЖНО: ожидает epoch в СЕКУНДАХ. Avito SERP-поле `sortTimeStamp` отдаёт epoch в
|
||
МИЛЛИСЕКУНДАХ — вызывающий код обязан поделить на 1000 до вызова (см.
|
||
`avito.py::_build_sort_timestamp_map`).
|
||
"""
|
||
if not ts:
|
||
return None
|
||
try:
|
||
return datetime.fromtimestamp(float(ts), tz=MSK).date()
|
||
except (OSError, OverflowError, ValueError):
|
||
logger.warning("Не удалось конвертировать unix timestamp: %r", ts)
|
||
return None
|