65 lines
2.6 KiB
Python
65 lines
2.6 KiB
Python
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
|
||
|