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