Improved handling of Time type.
still needs test, doc update
This commit is contained in:
@@ -48,15 +48,13 @@ from cassandra.marshal import (int8_pack, int8_unpack,
|
|||||||
int32_pack, int32_unpack, int64_pack, int64_unpack,
|
int32_pack, int32_unpack, int64_pack, int64_unpack,
|
||||||
float_pack, float_unpack, double_pack, double_unpack,
|
float_pack, float_unpack, double_pack, double_unpack,
|
||||||
varint_pack, varint_unpack)
|
varint_pack, varint_unpack)
|
||||||
from cassandra.util import OrderedMap, sortedset
|
from cassandra.util import OrderedMap, sortedset, Time
|
||||||
|
|
||||||
apache_cassandra_type_prefix = 'org.apache.cassandra.db.marshal.'
|
apache_cassandra_type_prefix = 'org.apache.cassandra.db.marshal.'
|
||||||
|
|
||||||
|
|
||||||
if six.PY3:
|
if six.PY3:
|
||||||
_number_types = frozenset((int, float))
|
_number_types = frozenset((int, float))
|
||||||
_time_types = frozenset((int,))
|
|
||||||
_date_types = frozenset((int,))
|
|
||||||
long = int
|
long = int
|
||||||
|
|
||||||
def _name_from_hex_string(encoded_name):
|
def _name_from_hex_string(encoded_name):
|
||||||
@@ -64,8 +62,6 @@ if six.PY3:
|
|||||||
return bin_str.decode('ascii')
|
return bin_str.decode('ascii')
|
||||||
else:
|
else:
|
||||||
_number_types = frozenset((int, long, float))
|
_number_types = frozenset((int, long, float))
|
||||||
_time_types = frozenset((int, long))
|
|
||||||
_date_types = frozenset((int, long))
|
|
||||||
_name_from_hex_string = unhexlify
|
_name_from_hex_string = unhexlify
|
||||||
|
|
||||||
|
|
||||||
@@ -633,7 +629,7 @@ class SimpleDateType(_CassandraType):
|
|||||||
def validate(cls, val):
|
def validate(cls, val):
|
||||||
if isinstance(val, six.string_types):
|
if isinstance(val, six.string_types):
|
||||||
val = cls.interpret_simpledate_string(val)
|
val = cls.interpret_simpledate_string(val)
|
||||||
elif (not isinstance(val, datetime.date)) and (type(val) not in _date_types):
|
elif (not isinstance(val, datetime.date)) and not isinstance(val, six.integer_types):
|
||||||
raise TypeError('SimpleDateType arg must be a datetime.date, unsigned integer, or string in the format YYYY-MM-DD')
|
raise TypeError('SimpleDateType arg must be a datetime.date, unsigned integer, or string in the format YYYY-MM-DD')
|
||||||
return val
|
return val
|
||||||
|
|
||||||
@@ -662,55 +658,24 @@ class SimpleDateType(_CassandraType):
|
|||||||
|
|
||||||
class TimeType(_CassandraType):
|
class TimeType(_CassandraType):
|
||||||
typename = 'time'
|
typename = 'time'
|
||||||
ONE_MICRO = 1000
|
|
||||||
ONE_MILLI = 1000 * ONE_MICRO
|
|
||||||
ONE_SECOND = 1000 * ONE_MILLI
|
|
||||||
ONE_MINUTE = 60 * ONE_SECOND
|
|
||||||
ONE_HOUR = 60 * ONE_MINUTE
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def validate(cls, val):
|
def validate(cls, val):
|
||||||
if isinstance(val, six.string_types):
|
if not isinstance(val, Time):
|
||||||
val = cls.interpret_timestring(val)
|
val = Time(val)
|
||||||
elif (not isinstance(val, datetime.time)) and (type(val) not in _time_types):
|
|
||||||
raise TypeError('TimeType arguments must be a string or whole number')
|
|
||||||
return val
|
return val
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def interpret_timestring(val):
|
|
||||||
try:
|
|
||||||
nano = 0
|
|
||||||
parts = val.split('.')
|
|
||||||
base_time = time.strptime(parts[0], "%H:%M:%S")
|
|
||||||
nano = (base_time.tm_hour * TimeType.ONE_HOUR +
|
|
||||||
base_time.tm_min * TimeType.ONE_MINUTE +
|
|
||||||
base_time.tm_sec * TimeType.ONE_SECOND)
|
|
||||||
|
|
||||||
if len(parts) > 1:
|
|
||||||
# right pad to 9 digits
|
|
||||||
nano_time_str = parts[1] + "0" * (9 - len(parts[1]))
|
|
||||||
nano += int(nano_time_str)
|
|
||||||
|
|
||||||
return nano
|
|
||||||
except ValueError:
|
|
||||||
raise ValueError("can't interpret %r as a time" % (val,))
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def serialize(val, protocol_version):
|
def serialize(val, protocol_version):
|
||||||
# Values of the @time@ type are encoded as 64-bit signed integers
|
|
||||||
# representing the number of nanoseconds since midnight.
|
|
||||||
try:
|
try:
|
||||||
nano = (val.hour * TimeType.ONE_HOUR +
|
nano = val.nanosecond_time
|
||||||
val.minute * TimeType.ONE_MINUTE +
|
|
||||||
val.second * TimeType.ONE_SECOND +
|
|
||||||
val.microsecond * TimeType.ONE_MICRO)
|
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
nano = val
|
nano = Time(val).nanosecond_time
|
||||||
return int64_pack(nano)
|
return int64_pack(nano)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def deserialize(byts, protocol_version):
|
def deserialize(byts, protocol_version):
|
||||||
return int64_unpack(byts)
|
return Time(int64_unpack(byts))
|
||||||
|
|
||||||
|
|
||||||
class UTF8Type(_CassandraType):
|
class UTF8Type(_CassandraType):
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import types
|
|||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from cassandra.util import OrderedDict, OrderedMap, sortedset
|
from cassandra.util import OrderedDict, OrderedMap, sortedset, Time
|
||||||
|
|
||||||
if six.PY3:
|
if six.PY3:
|
||||||
long = int
|
long = int
|
||||||
@@ -75,6 +75,7 @@ class Encoder(object):
|
|||||||
datetime.datetime: self.cql_encode_datetime,
|
datetime.datetime: self.cql_encode_datetime,
|
||||||
datetime.date: self.cql_encode_date,
|
datetime.date: self.cql_encode_date,
|
||||||
datetime.time: self.cql_encode_time,
|
datetime.time: self.cql_encode_time,
|
||||||
|
Time: self.cql_encode_time,
|
||||||
dict: self.cql_encode_map_collection,
|
dict: self.cql_encode_map_collection,
|
||||||
OrderedDict: self.cql_encode_map_collection,
|
OrderedDict: self.cql_encode_map_collection,
|
||||||
OrderedMap: self.cql_encode_map_collection,
|
OrderedMap: self.cql_encode_map_collection,
|
||||||
|
|||||||
@@ -653,3 +653,97 @@ class OrderedMap(Mapping):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def _serialize_key(key):
|
def _serialize_key(key):
|
||||||
return cPickle.dumps(key)
|
return cPickle.dumps(key)
|
||||||
|
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
import time
|
||||||
|
|
||||||
|
if six.PY3:
|
||||||
|
long = int
|
||||||
|
|
||||||
|
|
||||||
|
class Time(object):
|
||||||
|
'''
|
||||||
|
Idealized time, independent of day.
|
||||||
|
|
||||||
|
Up to nanosecond resolution
|
||||||
|
'''
|
||||||
|
|
||||||
|
MICRO = 1000
|
||||||
|
MILLI = 1000 * MICRO
|
||||||
|
SECOND = 1000 * MILLI
|
||||||
|
MINUTE = 60 * SECOND
|
||||||
|
HOUR = 60 * MINUTE
|
||||||
|
DAY = 24 * HOUR
|
||||||
|
|
||||||
|
nanosecond_time = 0
|
||||||
|
|
||||||
|
def __init__(self, value):
|
||||||
|
if isinstance(value, six.integer_types):
|
||||||
|
self._from_timestamp(value)
|
||||||
|
elif isinstance(value, datetime.time):
|
||||||
|
self._from_time(value)
|
||||||
|
elif isinstance(value, six.string_types):
|
||||||
|
self._from_timestring(value)
|
||||||
|
else:
|
||||||
|
raise TypeError('Time arguments must be a whole number, datetime.time, or string')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def hour(self):
|
||||||
|
return self.nanosecond_time // Time.HOUR
|
||||||
|
|
||||||
|
@property
|
||||||
|
def minute(self):
|
||||||
|
minutes = self.nanosecond_time // Time.MINUTE
|
||||||
|
return minutes % 60
|
||||||
|
|
||||||
|
@property
|
||||||
|
def second(self):
|
||||||
|
seconds = self.nanosecond_time // Time.SECOND
|
||||||
|
return seconds % 60
|
||||||
|
|
||||||
|
@property
|
||||||
|
def nanosecond(self):
|
||||||
|
return self.nanosecond_time % Time.SECOND
|
||||||
|
|
||||||
|
def _from_timestamp(self, t):
|
||||||
|
if t >= Time.DAY:
|
||||||
|
raise ValueError("value must be less than number of nanoseconds in a day (%d)" % Time.DAY)
|
||||||
|
self.nanosecond_time = t
|
||||||
|
|
||||||
|
def _from_timestring(self, s):
|
||||||
|
try:
|
||||||
|
parts = s.split('.')
|
||||||
|
base_time = time.strptime(parts[0], "%H:%M:%S")
|
||||||
|
self.nanosecond_time = (base_time.tm_hour * Time.HOUR +
|
||||||
|
base_time.tm_min * Time.MINUTE +
|
||||||
|
base_time.tm_sec * Time.SECOND)
|
||||||
|
|
||||||
|
if len(parts) > 1:
|
||||||
|
# right pad to 9 digits
|
||||||
|
nano_time_str = parts[1] + "0" * (9 - len(parts[1]))
|
||||||
|
self.nanosecond_time += int(nano_time_str)
|
||||||
|
|
||||||
|
except ValueError:
|
||||||
|
raise ValueError("can't interpret %r as a time" % (s,))
|
||||||
|
|
||||||
|
def _from_time(self, t):
|
||||||
|
self.nanosecond_time = (t.hour * Time.HOUR +
|
||||||
|
t.minute * Time.MINUTE +
|
||||||
|
t.second * Time.SECOND +
|
||||||
|
t.microsecond * Time.MICRO)
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
if isinstance(other, Time):
|
||||||
|
return self.nanosecond_time == other.nanosecond_time
|
||||||
|
|
||||||
|
return self.nanosecond_time % Time.MICRO == 0 and \
|
||||||
|
datetime.time(hour=self.hour, minute=self.minute, second=self.second,
|
||||||
|
microsecond=self.nanosecond // Time.MICRO) == other
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "Time(%s)" % self.nanosecond_time
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "%02d:%02d:%02d.%09d" % (self.hour, self.minute,
|
||||||
|
self.second, self.nanosecond)
|
||||||
|
|||||||
Reference in New Issue
Block a user