[Bug] precision: datetime: дедупликация падает с TypeError (naive vs aware datetime) #58

Closed
opened 2026-07-16 18:25:01 +07:00 by artem.kokos · 0 comments
Owner

Проблема

При period.precision: datetime и заполненном period.last_used.to дедупликация time entries падает с ошибкой:

❌ Unexpected error: can't compare offset-naive and offset-aware datetimes

Цепочка причин:

  1. _compute_dedup_cutoff() (redmine_reporter/cli.py:126-129) приводит cutoff к timezone-aware:
dt = datetime.fromisoformat(last_to.replace("Z", "+00:00"))
if dt.tzinfo is None:
    dt = dt.replace(tzinfo=timezone.utc)
  1. python-redmine возвращает created_on/updated_on как naive datetime: redminelib парсит "2026-07-10T12:00:00Z" через datetime.strptime(value, "%Y-%m-%dT%H:%M:%SZ") (redminelib/resources/base.py), а tzinfo проставляется только если в конструктор Redmine(...) передан параметр timezone — проект его не передаёт (client.py:52-59).

  2. Сравнение created >= dedup_before в цикле дедупликации (redmine_reporter/client.py:279-294) находится вне try-блока (try — строки 258-274), поэтому TypeError не оборачивается в RedmineAPIError, а улетает в общий except Exception в CLI с непонятным «Unexpected error».

Вторая проблема того же участка: --commit сохраняет last_used.to как naive локальное время:

# cli.py:523-528
now = dt_mod.now().isoformat(timespec="seconds")

При следующем запуске _compute_dedup_cutoff интерпретирует эту строку как UTC (replace(tzinfo=timezone.utc)). Для часового пояса ≠ UTC cutoff смещается на величину смещения зоны — записи могут дублироваться или теряться.

Пример воспроизведения

# ~/.config/redmine-reporter/config.yml
period:
  precision: datetime
  last_used:
    from: "2026-07-01T00:00:00"
    to: "2026-07-10T12:00:00"
redmine-reporter --compact   # без --date
# → ❌ Unexpected error: can't compare offset-naive and offset-aware datetimes

Подтверждено экспериментом: _parse_datetime(naive_dt) >= aware_cutoffTypeError.

Рекомендации

  1. Нормализовать обе стороны сравнения к aware UTC: при парсинге created_on/updated_on в _parse_datetime трактовать naive как UTC (redminelib отдаёт именно UTC-время в формате ...Z).
  2. В --commit сохранять aware UTC: datetime.now(timezone.utc).isoformat(timespec="seconds").
  3. Перенести цикл дедупликации внутрь try-блока или обернуть его ошибки в RedmineAPIError с понятным сообщением.
  4. Тесты: дедупликация с naive created_on против aware cutoff; roundtrip --commit → чтение cutoff в часовом поясе ≠ UTC.

Связанные места

  • redmine_reporter/cli.py:111-131 (_compute_dedup_cutoff)
  • redmine_reporter/cli.py:523-528 (сохранение last_used.to в --commit)
  • redmine_reporter/client.py:258-294 (try-блок и цикл дедупликации)
  • redmine_reporter/client.py (_parse_datetime)
  • Задачи #47 (datetime precision), #44 (--commit)

Критерии приёмки

  • Конфиг из примера выше не приводит к крашу; дедупликация работает.
  • last_used.to сохраняется в UTC с tzinfo; cutoff не зависит от локального часового пояса.
  • Ошибки дедупликации оборачиваются в RedmineAPIError с понятным текстом.
  • Добавлены тесты на naive/aware и timezone roundtrip.
## Проблема При `period.precision: datetime` и заполненном `period.last_used.to` дедупликация time entries падает с ошибкой: ``` ❌ Unexpected error: can't compare offset-naive and offset-aware datetimes ``` Цепочка причин: 1. `_compute_dedup_cutoff()` (`redmine_reporter/cli.py:126-129`) приводит cutoff к timezone-aware: ```python dt = datetime.fromisoformat(last_to.replace("Z", "+00:00")) if dt.tzinfo is None: dt = dt.replace(tzinfo=timezone.utc) ``` 2. `python-redmine` возвращает `created_on`/`updated_on` как **naive** datetime: redminelib парсит `"2026-07-10T12:00:00Z"` через `datetime.strptime(value, "%Y-%m-%dT%H:%M:%SZ")` (redminelib/resources/base.py), а `tzinfo` проставляется только если в конструктор `Redmine(...)` передан параметр `timezone` — проект его не передаёт (`client.py:52-59`). 3. Сравнение `created >= dedup_before` в цикле дедупликации (`redmine_reporter/client.py:279-294`) находится **вне** try-блока (try — строки 258-274), поэтому `TypeError` не оборачивается в `RedmineAPIError`, а улетает в общий `except Exception` в CLI с непонятным «Unexpected error». Вторая проблема того же участка: `--commit` сохраняет `last_used.to` как **naive локальное** время: ```python # cli.py:523-528 now = dt_mod.now().isoformat(timespec="seconds") ``` При следующем запуске `_compute_dedup_cutoff` интерпретирует эту строку как UTC (`replace(tzinfo=timezone.utc)`). Для часового пояса ≠ UTC cutoff смещается на величину смещения зоны — записи могут дублироваться или теряться. ## Пример воспроизведения ```yaml # ~/.config/redmine-reporter/config.yml period: precision: datetime last_used: from: "2026-07-01T00:00:00" to: "2026-07-10T12:00:00" ``` ```bash redmine-reporter --compact # без --date # → ❌ Unexpected error: can't compare offset-naive and offset-aware datetimes ``` Подтверждено экспериментом: `_parse_datetime(naive_dt) >= aware_cutoff` → `TypeError`. ## Рекомендации 1. Нормализовать обе стороны сравнения к aware UTC: при парсинге `created_on`/`updated_on` в `_parse_datetime` трактовать naive как UTC (redminelib отдаёт именно UTC-время в формате `...Z`). 2. В `--commit` сохранять aware UTC: `datetime.now(timezone.utc).isoformat(timespec="seconds")`. 3. Перенести цикл дедупликации внутрь try-блока или обернуть его ошибки в `RedmineAPIError` с понятным сообщением. 4. Тесты: дедупликация с naive `created_on` против aware cutoff; roundtrip `--commit` → чтение cutoff в часовом поясе ≠ UTC. ## Связанные места - `redmine_reporter/cli.py:111-131` (`_compute_dedup_cutoff`) - `redmine_reporter/cli.py:523-528` (сохранение `last_used.to` в `--commit`) - `redmine_reporter/client.py:258-294` (try-блок и цикл дедупликации) - `redmine_reporter/client.py` (`_parse_datetime`) - Задачи #47 (datetime precision), #44 (--commit) ## Критерии приёмки - [ ] Конфиг из примера выше не приводит к крашу; дедупликация работает. - [ ] `last_used.to` сохраняется в UTC с tzinfo; cutoff не зависит от локального часового пояса. - [ ] Ошибки дедупликации оборачиваются в `RedmineAPIError` с понятным текстом. - [ ] Добавлены тесты на naive/aware и timezone roundtrip.
artem.kokos added the bugcli labels 2026-07-16 18:25:01 +07:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: artem.kokos/redmine-reporter#58