Add support for a timezone type.

This commit is contained in:
Ryan Leckey
2013-07-26 01:12:47 -07:00
parent df5d6d1880
commit 175176c82b
3 changed files with 115 additions and 1 deletions

View File

@@ -42,7 +42,8 @@ extras_require = {
],
'password': ['passlib >= 1.6, < 2.0'],
'color': ['colour>=0.0.4'],
'ipaddress': ['ipaddr'] if not PY3 else []
'ipaddress': ['ipaddr'] if not PY3 else [],
'timezone': ['python-dateutil']
}

View File

@@ -0,0 +1,74 @@
import six
from sqlalchemy import types
from sqlalchemy_utils import ImproperlyConfigured
class TimezoneType(types.TypeDecorator):
"""
Changes Timezone objects to a string representation on the way in and
changes them back to Timezone objects on the way out.
"""
impl = types.CHAR(50)
python_type = None
def __init__(self, backend='dateutil'):
"""
:param backend: Whether to use 'dateutil' or 'pytz' for timezones.
"""
self.backend = backend
if backend == 'dateutil':
try:
from dateutil.tz import tzfile
from dateutil.zoneinfo import gettz
self.python_type = tzfile
self._to = gettz
self._from = lambda x: x._filename
except ImportError:
raise ImproperlyConfigured(
"'python-dateutil' is required to use the "
"'dateutil' backend for 'TimezoneType'"
)
elif backend == 'pytz':
try:
from pytz import tzfile, timezone
self.python_type = tzfile.DstTzInfo
self._to = timezone
self._from = six.text_type
except ImportError:
raise ImproperlyConfigured(
"'pytz' is required to use the 'pytz' backend "
"for 'TimezoneType'"
)
else:
raise ImproperlyConfigured(
"'pytz' or 'dateutil' are the backends supported for "
"'TimezoneType'"
)
def _coerce(self, value):
if value 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 coercion_listener(self, target, value, oldvalue, initiator):
return self._coerce(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

39
tests/test_timezone.py Normal file
View File

@@ -0,0 +1,39 @@
from pytest import mark
import six
import sqlalchemy as sa
from sqlalchemy_utils.types import timezone
from tests import TestCase
try:
import dateutil
except ImportError:
dateutil = None
@mark.skipif('dateutil is None')
class TestIPAddressType(TestCase):
def create_models(self):
class Visitor(self.Base):
__tablename__ = 'document'
id = sa.Column(sa.Integer, primary_key=True)
timezone = sa.Column(timezone.TimezoneType)
def __repr__(self):
return 'Visitor(%r)' % self.id
self.Visitor = Visitor
def test_parameter_processing(self):
visitor = self.Visitor(
timezone=u'America/Los_Angeles'
)
self.session.add(visitor)
self.session.commit()
visitor = self.session.query(self.Visitor).filter_by(
timezone='America/Los_Angeles').first()
assert visitor is not None