You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

65 lines
1.8 KiB
Python

import re
from datetime import datetime, timezone
def normalize_posted_text(text: str | None) -> datetime | None:
"""Parse relative posted-date strings into UTC datetimes.
Returns None if the text cannot be parsed.
"""
if not text:
return None
t = text.strip().lower()
now = datetime.now(timezone.utc)
# Exact matches
if t in ("today", "just posted", "posted today", "new", "1d", "0d"):
return now.replace(hour=0, minute=0, second=0, microsecond=0)
# "X hours ago" / "X hour ago"
m = re.search(r"(\d+)\s*hours?\s*ago", t)
if m:
from datetime import timedelta
return now - timedelta(hours=int(m.group(1)))
# "1 day ago" / "2 days ago"
m = re.search(r"(\d+)\s*days?\s*ago", t)
if m:
from datetime import timedelta
days = int(m.group(1))
if days <= 1:
return now.replace(hour=0, minute=0, second=0, microsecond=0)
return now - timedelta(days=days)
# "yesterday"
if "yesterday" in t:
from datetime import timedelta
return now - timedelta(days=1)
# "X minutes ago"
m = re.search(r"(\d+)\s*minutes?\s*ago", t)
if m:
from datetime import timedelta
return now - timedelta(minutes=int(m.group(1)))
# ISO date
m = re.match(r"(\d{4}-\d{2}-\d{2})", t)
if m:
try:
return datetime.strptime(m.group(1), "%Y-%m-%d").replace(tzinfo=timezone.utc)
except ValueError:
pass
return None
def is_recent(text: str | None, max_days: int = 1) -> bool:
"""Return True if posted_text represents a posting within max_days days."""
dt = normalize_posted_text(text)
if dt is None:
return False
from datetime import timedelta
cutoff = datetime.now(timezone.utc) - timedelta(days=max_days)
return dt >= cutoff