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
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
|