Source code for sqlalchemy_utils.types.timezone

from sqlalchemy import types

from ..exceptions import ImproperlyConfigured
from .scalar_coercible import ScalarCoercible

[docs]class TimezoneType(ScalarCoercible, types.TypeDecorator): """ TimezoneType provides a way for saving timezones objects into database. TimezoneType saves timezone objects as strings on the way in and converts them back to objects when querying the database. :: from sqlalchemy_utils import TimezoneType class User(Base): __tablename__ = 'user' # Pass backend='pytz' to change it to use pytz. Other values: # 'dateutil' (default), and 'zoneinfo'. timezone = sa.Column(TimezoneType(backend='pytz')) :param backend: Whether to use 'dateutil', 'pytz' or 'zoneinfo' for timezones. 'zoneinfo' uses the standard library module in Python 3.9+, but requires the external 'backports.zoneinfo' package for older Python versions. """ impl = types.Unicode(50) python_type = None cache_ok = True def __init__(self, backend='dateutil'): self.backend = backend if backend == 'dateutil': try: from import tzfile from dateutil.zoneinfo import get_zonefile_instance self.python_type = tzfile self._to = get_zonefile_instance().zones.get self._from = lambda x: str(x._filename) except ImportError: raise ImproperlyConfigured( "'python-dateutil' is required to use the " "'dateutil' backend for 'TimezoneType'" ) elif backend == 'pytz': try: from pytz import timezone from pytz.tzinfo import BaseTzInfo self.python_type = BaseTzInfo self._to = timezone self._from = str except ImportError: raise ImproperlyConfigured( "'pytz' is required to use the 'pytz' backend " "for 'TimezoneType'" ) elif backend == "zoneinfo": try: import zoneinfo except ImportError: try: from backports import zoneinfo except ImportError: raise ImproperlyConfigured( "'backports.zoneinfo' is required to use " "the 'zoneinfo' backend for 'TimezoneType'" "on Python version < 3.9" ) self.python_type = zoneinfo.ZoneInfo self._to = zoneinfo.ZoneInfo self._from = str else: raise ImproperlyConfigured( "'pytz', 'dateutil' or 'zoneinfo' are the backends " "supported for 'TimezoneType'" ) def _coerce(self, value): if value is not None and not isinstance(value, self.python_type): obj = self._to(value) if obj is None: raise ValueError("unknown time zone '%s'" % value) return obj return value def process_bind_param(self, value, dialect): return self._from(self._coerce(value)) if value else None def process_result_value(self, value, dialect): return self._to(value) if value else None