RU /EN
Обсудить проект
← Избранные проекты
Разбор проекта

timesense

Детерминированный парсер естественного языка для дат, событий и повторов — без LLM и без ML. «каждый второй вторник в 18:30 ретро» → структурированное событие, RFC 5545 rrule или готовый .ics. Русский и английский, zero-dependency, меньше 1 мс на фразу.

Что это

Библиотека, которая превращает фразу на естественном языке в структурированное событие. Не только дату и время: тип события, заголовок, место, продолжительность, правило повтора и дедлайн.

«каждый второй вторник в 18:30 ретро в переговорке»
  → событие · заголовок «ретро» · место «в переговорке»
  → 18:30 · FREQ=MONTHLY;BYDAY=2TU

Результат выгружается в JSON, в rrule по RFC 5545 или в готовый .ics, который открывается в Google Calendar, Apple Calendar и Outlook.

Русский и английский, определение языка автоматическое. Чистый Python 3.9–3.13, ноль обязательных зависимостей.

Почему появился

Задача возникает каждый раз, когда есть голосовой ввод или бот: человек говорит «перенеси на следующий вторник к шести», а системе нужна дата, время и правило повтора.

Готовые парсеры дат — dateparser, parsedatetime — извлекают datetime и на этом останавливаются. Они не скажут, что это повторяющееся событие, не отдадут rrule и не соберут .ics. Всё остальное приходится писать самому.

Второй путь — отправить фразу в LLM. И вот здесь начинается настоящая проблема.

Почему не LLM

Календарной автоматизации нужна детерминированность: одна и та же фраза обязана давать одно и то же событие каждый раз. Не «почти всегда», а всегда.

Модель этого не гарантирует. На той же фразе она может вернуть другую дату после обновления версии, при другой температуре или просто в другой раз. Отладить такое поведение нельзя: нет места, куда можно посмотреть и увидеть, почему получился именно этот ответ.

Внутри timesense нет машинного обучения — только словари и регулярные выражения. Отсюда всё остальное:

Показательtimesense
Обязательные зависимости0 — только стандартная библиотека
Модели и GPUне нужны
Холодный старт~0,2 с
Памятьнесколько МБ
Задержкаменьше 1 мс на фразу, 1 000+ фраз в секунду на одном скромном ядре
Одинаковый вход → одинаковый выходда, всегда

Практическое следствие: парсер можно вызывать на каждой реплике живой транскрипции, прямо внутри конвейера, и он работает на самой дешёвой VPS, на плате уровня Raspberry Pi или внутри serverless-функции.

Что внутри

Три типа результата, выбираются автоматически:

  • точка во времени → ReminderResult
  • интервал с началом и концом → CalendarResult
  • период, дедлайн, открытое начало или размытый срок → TaskResult

Что разбирается. Время (в 17:00, без пяти десять, half past ten), относительные сроки (через полтора часа, через 2,5 часа с десятичной запятой, in forty five minutes), даты (первого сентября, 15 числа, ISO), диапазоны (с девяти до пяти, через полночь), повторы (по будням, каждые 2 недели, каждое 15-е число), дедлайны (до конца марта, не позднее 20-го), продолжительность и место.

Рабочий календарь. Можно загрузить праздники и перенесённые рабочие субботы — тогда «последний рабочий день месяца» считается правильно, а не как «последний Пн–Пт».

Арифметика рабочих дней. за два рабочих дня до конца месяца разрешается в конкретную дату с учётом загруженного календаря.

Осознанные ограничения

Часовые пояса не поддерживаются: все datetime — наивные, без tzinfo. Фраза с зоной (в 10 мск, GMT+3) намеренно возвращает None, а не догадку.

Это выбор, а не недоделка. Молча вернуть время без зоны, когда зона в тексте была, — худший из возможных вариантов: ошибка обнаружится в календаре пользователя, а не в коде.

Что получилось

Стабильная версия 1.0.0 с зафиксированным публичным API, MIT, пакет в PyPI, тесты в репозитории и отдельные наборы для русской и английской грамматики.