Fix RemnaWave sync timezone handling

This commit is contained in:
Egor
2025-10-08 03:33:32 +03:00
parent 99c882110a
commit 48fe845ff4
2 changed files with 42 additions and 19 deletions

View File

@@ -8,7 +8,7 @@ from collections import defaultdict
from datetime import time
from typing import List, Optional, Union, Dict
from pydantic_settings import BaseSettings
from pydantic import field_validator, Field
from pydantic import field_validator, Field, AliasChoices
from pathlib import Path
@@ -64,6 +64,8 @@ class Settings(BaseSettings):
REMNAWAVE_AUTH_TYPE: str = "api_key"
REMNAWAVE_USER_DESCRIPTION_TEMPLATE: str = "Bot user: {full_name} {username}"
REMNAWAVE_USER_DELETE_MODE: str = "delete" # "delete" или "disable"
TIMEZONE: str = Field(default="UTC", validation_alias=AliasChoices("TIMEZONE", "TZ"))
TRIAL_DURATION_DAYS: int = 3
TRIAL_TRAFFIC_LIMIT_GB: int = 10
@@ -414,6 +416,10 @@ class Settings(BaseSettings):
"auth_type": self.REMNAWAVE_AUTH_TYPE
}
def get_timezone(self) -> str:
timezone_value = (self.TIMEZONE or "UTC").strip()
return timezone_value or "UTC"
def get_pal24_sbp_button_text(self, fallback: str) -> str:
value = (self.PAL24_SBP_BUTTON_TEXT or "").strip()
return value or fallback

View File

@@ -3,7 +3,8 @@ from contextlib import asynccontextmanager
from typing import Dict, List, Any, Optional
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import delete
from datetime import datetime, timedelta
from datetime import datetime, timedelta, timezone
from zoneinfo import ZoneInfo
import re
from app.config import settings
@@ -51,6 +52,8 @@ class RemnaWaveService:
password=auth_params.get("password")
)
self._timezone = self._resolve_timezone()
@property
def is_configured(self) -> bool:
return self._config_error is None
@@ -72,33 +75,47 @@ class RemnaWaveService:
async with self.api as api:
yield api
def _parse_remnawave_date(self, date_str: str) -> datetime:
if not date_str:
return datetime.utcnow() + timedelta(days=30)
def _resolve_timezone(self) -> ZoneInfo:
timezone_name = settings.get_timezone()
try:
return ZoneInfo(timezone_name)
except Exception:
logger.warning(
f"⚠️ Не удалось загрузить временную зону '{timezone_name}', используется UTC"
)
return ZoneInfo("UTC")
def _parse_remnawave_date(self, date_str: str) -> datetime:
target_timezone = self._timezone
if not date_str:
return (datetime.now(target_timezone) + timedelta(days=30)).replace(tzinfo=None)
try:
cleaned_date = date_str.strip()
if cleaned_date.endswith('Z'):
cleaned_date = cleaned_date[:-1] + '+00:00'
if '+00:00+00:00' in cleaned_date:
cleaned_date = cleaned_date.replace('+00:00+00:00', '+00:00')
cleaned_date = re.sub(r'(\+\d{2}:\d{2})\+\d{2}:\d{2}$', r'\1', cleaned_date)
parsed_date = datetime.fromisoformat(cleaned_date)
if parsed_date.tzinfo is not None:
parsed_date = parsed_date.replace(tzinfo=None)
logger.debug(f"Успешно распарсена дата: {date_str} -> {parsed_date}")
return parsed_date
if parsed_date.tzinfo is None:
parsed_date = parsed_date.replace(tzinfo=timezone.utc)
localized_date = parsed_date.astimezone(target_timezone)
result = localized_date.replace(tzinfo=None)
logger.debug(f"Успешно распарсена дата: {date_str} -> {result}")
return result
except Exception as e:
logger.warning(f"⚠️ Не удалось распарсить дату '{date_str}': {e}. Используем дефолтную дату.")
return datetime.utcnow() + timedelta(days=30)
return (datetime.now(target_timezone) + timedelta(days=30)).replace(tzinfo=None)
async def get_system_statistics(self) -> Dict[str, Any]:
try: