This commit is contained in:
kandrusyak
2026-07-25 15:07:53 +03:00
commit 6667e0d253
40 changed files with 3258 additions and 0 deletions

View File

@@ -0,0 +1,64 @@
import re
from datetime import datetime, timedelta, timezone
from zoneinfo import ZoneInfo
from .models import ReminderParseResult
from .time_utils import utc_now
def unit_to_timedelta(amount: int, unit: str) -> timedelta | None:
normalized = unit.lower().strip(".")
if normalized in {"s", "sec", "secs", "second", "seconds", "с", "сек", "секунд"}:
return timedelta(seconds=amount)
if normalized in {"m", "min", "mins", "minute", "minutes", "м"} or normalized.startswith("мин"):
return timedelta(minutes=amount)
if normalized in {"h", "hr", "hrs", "hour", "hours", "ч"} or normalized.startswith("час"):
return timedelta(hours=amount)
if normalized in {"d", "day", "days", "д"} or normalized.startswith(("дн", "ден")):
return timedelta(days=amount)
return None
def parse_reminder(raw_text: str, tz: ZoneInfo) -> ReminderParseResult | None:
text = raw_text.strip()
if not text:
return None
relative_match = re.match(r"^(?:через\s+)?(\d+)\s*([a-zа-я.]+)\s+(.+)$", text, re.IGNORECASE)
if relative_match:
amount = int(relative_match.group(1))
delta = unit_to_timedelta(amount, relative_match.group(2))
reminder_text = relative_match.group(3).strip()
if delta and reminder_text:
return ReminderParseResult(utc_now() + delta, reminder_text)
now_local = datetime.now(tz)
absolute_patterns = (
(r"^(\d{4}-\d{2}-\d{2})\s+(\d{1,2}:\d{2})\s+(.+)$", "%Y-%m-%d %H:%M"),
(r"^(\d{1,2}\.\d{1,2}\.\d{4})\s+(\d{1,2}:\d{2})\s+(.+)$", "%d.%m.%Y %H:%M"),
)
for pattern, date_format in absolute_patterns:
match = re.match(pattern, text)
if not match:
continue
naive = datetime.strptime(f"{match.group(1)} {match.group(2)}", date_format)
remind_at = naive.replace(tzinfo=tz)
reminder_text = match.group(3).strip()
if reminder_text:
return ReminderParseResult(remind_at.astimezone(timezone.utc), reminder_text)
time_match = re.match(r"^(\d{1,2}:\d{2})\s+(.+)$", text)
if time_match:
hour, minute = (int(part) for part in time_match.group(1).split(":", 1))
if 0 <= hour <= 23 and 0 <= minute <= 59:
remind_at = now_local.replace(hour=hour, minute=minute, second=0, microsecond=0)
if remind_at <= now_local:
remind_at += timedelta(days=1)
reminder_text = time_match.group(2).strip()
if reminder_text:
return ReminderParseResult(remind_at.astimezone(timezone.utc), reminder_text)
return None