brain / src /utils /dates.py
Leon4gr45's picture
Upload folder using huggingface_hub (part 3)
a55ea57 verified
Raw History Blame Contribute Delete
4.31 kB
import calendar
from datetime import datetime, timedelta, timezone
from typing import Optional
import re
_DATE_INPUT_FORMATS = (
"%d/%m/%Y",
"%Y-%m-%d",
"%Y/%m/%d",
"%d-%m-%Y",
"%B %d, %Y",
"%b %d, %Y",
"%B %d %Y",
"%b %d %Y",
"%d %B %Y",
"%d %b %Y",
"%B-%d-%Y",
"%b-%d-%Y",
"%Y-%m-%dT%H:%M:%S",
"%Y-%m-%dT%H:%M:%S%z",
"%Y-%m-%dT%H:%M:%S.%f",
"%Y-%m-%dT%H:%M:%S.%f%z",
"%Y-%m-%d %H:%M:%S",
"%Y-%m-%d %H:%M:%S%z",
"%Y-%m-%d %H:%M:%S.%f",
"%Y-%m-%d %H:%M:%S.%f%z",
"%I:%M %p on %d %B, %Y",
"%I:%M %p on %d %b, %Y",
)
_RELATIVE_PATTERNS = (
(re.compile(r"^yesterday$", re.I), lambda ref: ref - timedelta(days=1)),
(re.compile(r"^tomorrow$", re.I), lambda ref: ref + timedelta(days=1)),
(re.compile(r"^today$", re.I), lambda ref: ref),
(re.compile(r"^last\s+week$", re.I), lambda ref: ref - timedelta(days=7)),
(re.compile(r"^next\s+week$", re.I), lambda ref: ref + timedelta(days=7)),
(re.compile(r"^last\s+month$", re.I), lambda ref: _shift_months(ref, -1)),
(re.compile(r"^next\s+month$", re.I), lambda ref: _shift_months(ref, 1)),
(
re.compile(r"^(\d+)\s+days?\s+ago$", re.I),
lambda ref, m: ref - timedelta(days=int(m.group(1))),
),
(
re.compile(r"^in\s+(\d+)\s+days?$", re.I),
lambda ref, m: ref + timedelta(days=int(m.group(1))),
),
(
re.compile(r"^(\d+)\s+weeks?\s+ago$", re.I),
lambda ref, m: ref - timedelta(weeks=int(m.group(1))),
),
)
_WEEKDAY_NAMES = {
"monday": 0,
"tuesday": 1,
"wednesday": 2,
"thursday": 3,
"friday": 4,
"saturday": 5,
"sunday": 6,
}
def _shift_months(ref: datetime, delta: int) -> datetime:
month = ref.month - 1 + delta
year = ref.year + month // 12
month = month % 12 + 1
day = min(ref.day, [31, 29 if calendar.isleap(year) else 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month - 1])
return ref.replace(year=year, month=month, day=day)
def to_naive_utc(value: datetime) -> datetime:
if value.tzinfo is None:
return value
return value.astimezone(timezone.utc).replace(tzinfo=None)
def parse_date_string(value: Optional[str]) -> Optional[datetime]:
if not value or not isinstance(value, str):
return None
cleaned = value.strip()
for fmt in _DATE_INPUT_FORMATS:
try:
parsed = datetime.strptime(cleaned, fmt)
except ValueError:
continue
return to_naive_utc(parsed)
return None
def normalize_date_string(value: Optional[str]) -> Optional[str]:
if not value or not isinstance(value, str):
return value
parsed = parse_date_string(value)
if parsed is not None:
return parsed.strftime("%d/%m/%Y")
return value.strip()
def resolve_relative_date(
value: Optional[str],
reference_time: Optional[str | datetime] = None,
) -> Optional[str]:
if not value or not isinstance(value, str):
return value
cleaned = value.strip()
absolute = normalize_date_string(cleaned)
if absolute != cleaned or parse_date_string(cleaned) is not None:
return absolute
if reference_time is None:
return cleaned
if isinstance(reference_time, datetime):
ref = reference_time
else:
ref = parse_date_string(str(reference_time))
if ref is None:
return cleaned
for pattern, resolver in _RELATIVE_PATTERNS:
match = pattern.match(cleaned)
if not match:
continue
if match.lastindex:
resolved = resolver(ref, match)
else:
resolved = resolver(ref)
return resolved.strftime("%d/%m/%Y")
weekday_match = re.match(r"^(last|this|next)\s+(monday|tuesday|wednesday|thursday|friday|saturday|sunday)$", cleaned, re.I)
if weekday_match:
mode = weekday_match.group(1).lower()
target = _WEEKDAY_NAMES[weekday_match.group(2).lower()]
current = ref.weekday()
if mode == "this":
delta = (target - current) % 7
elif mode == "next":
delta = (target - current) % 7 or 7
else:
delta = -((current - target) % 7 or 7)
return (ref + timedelta(days=delta)).strftime("%d/%m/%Y")
return cleaned