Fix naive/aware datetime comparison for user expiration.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
orohi
2026-07-26 05:15:37 +03:00
co-authored by Cursor
parent 745b5e5d8c
commit 64079c1724
2 changed files with 44 additions and 15 deletions
+35 -8
View File
@@ -2,20 +2,44 @@
from __future__ import annotations
from datetime import datetime, timedelta
from typing import Optional
from datetime import datetime, timedelta, timezone
from typing import Optional, Union
def _parse_dt(value: Union[str, datetime, None]) -> Optional[datetime]:
if value is None or value == '':
return None
if isinstance(value, datetime):
return value
text = str(value).strip()
if not text:
return None
if text.endswith('Z'):
text = text[:-1] + '+00:00'
try:
return datetime.fromisoformat(text)
except Exception:
return None
def _as_utc(dt: datetime) -> datetime:
"""Normalize naive/aware datetimes for safe comparison."""
if dt.tzinfo is None:
# Treat naive wall time as local, then convert to UTC.
return dt.astimezone(timezone.utc)
return dt.astimezone(timezone.utc)
def user_is_expired(user: dict, *, now: Optional[datetime] = None) -> bool:
"""True when absolute expiration_date is in the past."""
exp_str = user.get('expiration_date')
if not exp_str:
exp_date = _parse_dt(user.get('expiration_date'))
if not exp_date:
return False
stamp = now if now is not None else datetime.now().astimezone()
try:
exp_date = datetime.fromisoformat(str(exp_str))
return _as_utc(stamp) > _as_utc(exp_date)
except Exception:
return False
return (now or datetime.now()) > exp_date
def maybe_start_user_expiration(data: dict, user_id: str, *, now: Optional[datetime] = None) -> Optional[str]:
@@ -35,8 +59,11 @@ def maybe_start_user_expiration(data: dict, user_id: str, *, now: Optional[datet
days = int(user.get('expiration_days') or 0)
if days <= 0:
return None
stamp = now or datetime.now()
stamp = now if now is not None else datetime.now()
expires = stamp + timedelta(days=days)
iso = expires.isoformat()
# Store without tz so UI/local comparisons stay consistent with manual dates
if expires.tzinfo is not None:
expires = expires.replace(tzinfo=None)
iso = expires.isoformat(timespec='seconds')
user['expiration_date'] = iso
return iso