115 lines
3.8 KiB
Python
115 lines
3.8 KiB
Python
import arrow
|
|
import calendar
|
|
from api_configs.configs import Config
|
|
from datetime import timedelta
|
|
|
|
|
|
class DateTimeLocal:
|
|
|
|
def __init__(self, timezone: str = None, is_client: bool = True):
|
|
if timezone and timezone not in Config.SUPPORTED_TIMEZONES:
|
|
raise ValueError(f"Unsupported timezone: {timezone}. Must be one of {Config.SUPPORTED_TIMEZONES}")
|
|
|
|
self.timezone = Config.SYSTEM_TIMEZONE
|
|
if is_client:
|
|
self.timezone = (timezone or Config.DEFAULT_TIMEZONE).replace("-", "+")
|
|
|
|
def find_last_day_of_month(self, date_value):
|
|
today = self.get(date_value).date()
|
|
_, last_day = calendar.monthrange(today.year, today.month)
|
|
return self.get(today.year, today.month, last_day, 23, 59, 59).to(self.timezone)
|
|
|
|
def find_first_day_of_month(self, date_value):
|
|
today = self.get(date_value).date()
|
|
return self.get(today.year, today.month, 1).to(self.timezone)
|
|
|
|
def get(self, *args):
|
|
return arrow.get(*args).to(str(self.timezone))
|
|
|
|
def now(self):
|
|
return arrow.now().to(str(self.timezone))
|
|
|
|
def shift(self, date, **kwargs):
|
|
return self.get(date).shift(**kwargs)
|
|
|
|
def date(self, date):
|
|
return self.get(date).date()
|
|
|
|
def time(self, date):
|
|
return self.get(date).time()
|
|
|
|
def string_date(self, date, splitter: str = "-"):
|
|
return str(self.get(date).date()).replace("-", splitter)
|
|
|
|
def string_time_only(self, date):
|
|
return self.get(date).format("HH:mm:ss")
|
|
|
|
def string_date_only(self, date):
|
|
return self.get(date).format("YYYY-MM-DD")
|
|
|
|
def to_timestamp(self, date):
|
|
"""Convert datetime to UTC timestamp"""
|
|
return self.get(date).timestamp()
|
|
|
|
def from_timestamp(self, timestamp):
|
|
"""Convert timestamp to timezone-aware datetime"""
|
|
return arrow.get(timestamp).to(str(self.timezone))
|
|
|
|
def is_timezone_aware(self, date):
|
|
"""Check if a date is timezone-aware"""
|
|
return self.get(date).tzinfo is not None
|
|
|
|
def standardize_timezone(self, date):
|
|
"""Ensure date is in the correct timezone"""
|
|
if not self.is_timezone_aware(date):
|
|
return self.get(date).to(str(self.timezone))
|
|
return self.get(date)
|
|
|
|
def get_expiry_time(self, **kwargs):
|
|
"""Get future time for cache expiry
|
|
Example: get_expiry_time(hours=1, minutes=30)
|
|
"""
|
|
return self.now().shift(**kwargs)
|
|
|
|
def is_expired(self, timestamp):
|
|
"""Check if a timestamp is expired"""
|
|
if not timestamp:
|
|
return True
|
|
return self.from_timestamp(timestamp) < self.now()
|
|
|
|
def get_cache_key(self, base_key, *args):
|
|
"""Generate a cache key with timezone info
|
|
Example: get_cache_key('user_profile', user_id, 'details')
|
|
"""
|
|
components = [str(base_key)]
|
|
components.extend(str(arg) for arg in args)
|
|
components.append(f"tz_{self.timezone}")
|
|
return ':'.join(components)
|
|
|
|
def format_for_db(self, date):
|
|
"""Format date for database storage"""
|
|
return self.get(date).format('YYYY-MM-DD HH:mm:ss.SSSZZ')
|
|
|
|
def parse_from_db(self, date_str):
|
|
"""Parse date from database format"""
|
|
if not date_str:
|
|
return None
|
|
return self.get(date_str)
|
|
|
|
def get_day_boundaries(self, date=None):
|
|
"""Get start and end of day in current timezone"""
|
|
dt = self.get(date) if date else self.now()
|
|
start = dt.floor('day')
|
|
end = dt.ceil('day')
|
|
return start, end
|
|
|
|
def get_month_boundaries(self, date=None):
|
|
"""Get start and end of month in current timezone"""
|
|
dt = self.get(date) if date else self.now()
|
|
start = dt.floor('month')
|
|
end = dt.ceil('month')
|
|
return start, end
|
|
|
|
client_arrow = DateTimeLocal(is_client=True)
|
|
system_arrow = DateTimeLocal(is_client=False)
|