cqle: introduce 'date' and 'time' column types
PYTHON-245
This commit is contained in:
@@ -19,9 +19,8 @@ import re
|
||||
import six
|
||||
import warnings
|
||||
|
||||
from cassandra.cqltypes import DateType
|
||||
from cassandra.encoder import cql_quote
|
||||
|
||||
from cassandra import util
|
||||
from cassandra.cqltypes import DateType, SimpleDateType
|
||||
from cassandra.cqlengine import ValidationError
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@@ -361,6 +360,10 @@ class Integer(Column):
|
||||
class TinyInt(Integer):
|
||||
"""
|
||||
Stores an 8-bit signed integer value
|
||||
|
||||
.. versionadded:: 2.6.0
|
||||
|
||||
requires C* 2.2+ and protocol v4+
|
||||
"""
|
||||
db_type = 'tinyint'
|
||||
|
||||
@@ -368,6 +371,10 @@ class TinyInt(Integer):
|
||||
class SmallInt(Integer):
|
||||
"""
|
||||
Stores a 16-bit signed integer value
|
||||
|
||||
.. versionadded:: 2.6.0
|
||||
|
||||
requires C* 2.2+ and protocol v4+
|
||||
"""
|
||||
db_type = 'smallint'
|
||||
|
||||
@@ -466,35 +473,43 @@ class DateTime(Column):
|
||||
|
||||
class Date(Column):
|
||||
"""
|
||||
*Note: this type is overloaded, and will likely be changed or removed to accommodate distinct date type
|
||||
in a future version*
|
||||
Stores a simple date, with no time-of-day
|
||||
|
||||
Stores a date value, with no time-of-day
|
||||
.. versionchanged:: 2.6.0
|
||||
|
||||
removed overload of Date and DateTime. DateTime is a drop-in replacement for legacy models
|
||||
|
||||
requires C* 2.2+ and protocol v4+
|
||||
"""
|
||||
db_type = 'timestamp'
|
||||
|
||||
def to_python(self, value):
|
||||
if value is None:
|
||||
return
|
||||
if isinstance(value, datetime):
|
||||
return value.date()
|
||||
elif isinstance(value, date):
|
||||
return value
|
||||
try:
|
||||
return datetime.utcfromtimestamp(value).date()
|
||||
except TypeError:
|
||||
return datetime.utcfromtimestamp(DateType.deserialize(value)).date()
|
||||
db_type = 'date'
|
||||
|
||||
def to_database(self, value):
|
||||
value = super(Date, self).to_database(value)
|
||||
if value is None:
|
||||
return
|
||||
if isinstance(value, datetime):
|
||||
value = value.date()
|
||||
if not isinstance(value, date):
|
||||
raise ValidationError("{} '{}' is not a date object".format(self.column_name, repr(value)))
|
||||
|
||||
return int((value - date(1970, 1, 1)).total_seconds() * 1000)
|
||||
# need to translate to int version because some dates are not representable in
|
||||
# string form (datetime limitation)
|
||||
d = value if isinstance(value, util.Date) else util.Date(value)
|
||||
return d.days_from_epoch + SimpleDateType.EPOCH_OFFSET_DAYS
|
||||
|
||||
|
||||
class Time(Column):
|
||||
"""
|
||||
Stores a timezone-naive time-of-day, with nanosecond precision
|
||||
|
||||
.. versionadded:: 2.6.0
|
||||
|
||||
requires C* 2.2+ and protocol v4+
|
||||
"""
|
||||
db_type = 'time'
|
||||
|
||||
def to_database(self, value):
|
||||
value = super(Time, self).to_database(value)
|
||||
if value is None:
|
||||
return
|
||||
# str(util.Time) yields desired CQL encoding
|
||||
return value if isinstance(value, util.Time) else util.Time(value)
|
||||
|
||||
|
||||
class UUID(Column):
|
||||
@@ -852,7 +867,7 @@ class UserDefinedType(Column):
|
||||
|
||||
def __init__(self, user_type, **kwargs):
|
||||
"""
|
||||
:param type user_type: specifies the :class:`~.UserType` model of the column
|
||||
:param type user_type: specifies the :class:`~.cqlengine.usertype.UserType` model of the column
|
||||
"""
|
||||
self.user_type = user_type
|
||||
self.db_type = "frozen<%s>" % user_type.type_name()
|
||||
|
@@ -638,27 +638,29 @@ class SimpleDateType(_CassandraType):
|
||||
typename = 'date'
|
||||
date_format = "%Y-%m-%d"
|
||||
|
||||
# Values of the 'date'` type are encoded as 32-bit unsigned integers
|
||||
# representing a number of days with epoch (January 1st, 1970) at the center of the
|
||||
# range (2^31).
|
||||
EPOCH_OFFSET_DAYS = 2 ** 31
|
||||
|
||||
@classmethod
|
||||
def validate(cls, val):
|
||||
if not isinstance(val, util.Date):
|
||||
val = util.Date(val)
|
||||
return val
|
||||
|
||||
@staticmethod
|
||||
def deserialize(byts, protocol_version):
|
||||
days = uint32_unpack(byts) - SimpleDateType.EPOCH_OFFSET_DAYS
|
||||
return util.Date(days)
|
||||
|
||||
@staticmethod
|
||||
def serialize(val, protocol_version):
|
||||
# Values of the 'date'` type are encoded as 32-bit unsigned integers
|
||||
# representing a number of days with epoch (January 1st, 1970) at the center of the
|
||||
# range (2^31).
|
||||
try:
|
||||
days = val.days_from_epoch
|
||||
except AttributeError:
|
||||
days = util.Date(val).days_from_epoch
|
||||
return uint32_pack(days + 2 ** 31)
|
||||
|
||||
@staticmethod
|
||||
def deserialize(byts, protocol_version):
|
||||
days = uint32_unpack(byts) - 2 ** 31
|
||||
return util.Date(days)
|
||||
return uint32_pack(days + SimpleDateType.EPOCH_OFFSET_DAYS)
|
||||
|
||||
|
||||
class ShortType(_CassandraType):
|
||||
@@ -682,6 +684,10 @@ class TimeType(_CassandraType):
|
||||
val = util.Time(val)
|
||||
return val
|
||||
|
||||
@staticmethod
|
||||
def deserialize(byts, protocol_version):
|
||||
return util.Time(int64_unpack(byts))
|
||||
|
||||
@staticmethod
|
||||
def serialize(val, protocol_version):
|
||||
try:
|
||||
@@ -690,10 +696,6 @@ class TimeType(_CassandraType):
|
||||
nano = util.Time(val).nanosecond_time
|
||||
return int64_pack(nano)
|
||||
|
||||
@staticmethod
|
||||
def deserialize(byts, protocol_version):
|
||||
return util.Time(int64_unpack(byts))
|
||||
|
||||
|
||||
class UTF8Type(_CassandraType):
|
||||
typename = 'text'
|
||||
|
@@ -917,7 +917,7 @@ class Time(object):
|
||||
|
||||
class Date(object):
|
||||
'''
|
||||
Idealized naive date: year, month, day
|
||||
Idealized date: year, month, day
|
||||
|
||||
Offers wider year range than datetime.date. For Dates that cannot be represented
|
||||
as a datetime.date (because datetime.MINYEAR, datetime.MAXYEAR), this type falls back
|
||||
@@ -997,5 +997,5 @@ class Date(object):
|
||||
dt = datetime_from_timestamp(self.seconds)
|
||||
return "%04d-%02d-%02d" % (dt.year, dt.month, dt.day)
|
||||
except:
|
||||
# If we overflow datetime.[MIN|M
|
||||
# If we overflow datetime.[MIN|MAX]
|
||||
return str(self.days_from_epoch)
|
||||
|
@@ -52,7 +52,7 @@ Columns of all types are initialized by passing :class:`.Column` attributes to t
|
||||
|
||||
.. autoclass:: Counter
|
||||
|
||||
.. autoclass:: Date(**kwargs)
|
||||
.. autoclass:: Date
|
||||
|
||||
.. autoclass:: DateTime(**kwargs)
|
||||
|
||||
@@ -70,10 +70,16 @@ Columns of all types are initialized by passing :class:`.Column` attributes to t
|
||||
|
||||
.. autoclass:: Set
|
||||
|
||||
.. autoclass:: SmallInt
|
||||
|
||||
.. autoclass:: Text
|
||||
|
||||
.. autoclass:: Time
|
||||
|
||||
.. autoclass:: TimeUUID(**kwargs)
|
||||
|
||||
.. autoclass:: TinyInt
|
||||
|
||||
.. autoclass:: UserDefinedType
|
||||
|
||||
.. autoclass:: UUID(**kwargs)
|
||||
|
Reference in New Issue
Block a user