Collect time functions in util, add uuid_from_time and min/max functions
PYTHON-99 Conflicts: cassandra/cqltypes.py cassandra/util.py
This commit is contained in:
@@ -34,24 +34,27 @@ from collections import namedtuple
|
|||||||
import datetime
|
import datetime
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
import io
|
import io
|
||||||
|
import logging
|
||||||
import re
|
import re
|
||||||
import socket
|
import socket
|
||||||
import time
|
import time
|
||||||
import sys
|
|
||||||
from uuid import UUID
|
|
||||||
|
|
||||||
import six
|
import six
|
||||||
from six.moves import range
|
from six.moves import range
|
||||||
|
import sys
|
||||||
|
from uuid import UUID
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
|
||||||
from cassandra.marshal import (int8_pack, int8_unpack,
|
from cassandra.marshal import (int8_pack, int8_unpack,
|
||||||
uint16_pack, uint16_unpack, uint32_pack, uint32_unpack,
|
uint16_pack, uint16_unpack, uint32_pack, uint32_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, Time
|
from cassandra import util
|
||||||
|
|
||||||
apache_cassandra_type_prefix = 'org.apache.cassandra.db.marshal.'
|
apache_cassandra_type_prefix = 'org.apache.cassandra.db.marshal.'
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
if six.PY3:
|
if six.PY3:
|
||||||
_number_types = frozenset((int, float))
|
_number_types = frozenset((int, float))
|
||||||
@@ -72,16 +75,17 @@ def trim_if_startswith(s, prefix):
|
|||||||
|
|
||||||
|
|
||||||
def unix_time_from_uuid1(u):
|
def unix_time_from_uuid1(u):
|
||||||
return (u.time - 0x01B21DD213814000) / 10000000.0
|
msg = "'cassandra.cqltypes.unix_time_from_uuid1' has moved to 'cassandra.util'. This entry point will be removed in the next major version."
|
||||||
|
warnings.warn(msg, DeprecationWarning)
|
||||||
|
log.warn(msg)
|
||||||
DATETIME_EPOC = datetime.datetime(1970, 1, 1)
|
return util.unix_time_from_uuid1(u)
|
||||||
|
|
||||||
|
|
||||||
def datetime_from_timestamp(timestamp):
|
def datetime_from_timestamp(timestamp):
|
||||||
# PYTHON-119: workaround for Windows
|
msg = "'cassandra.cqltypes.datetime_from_timestamp' has moved to 'cassandra.util'. This entry point will be removed in the next major version."
|
||||||
dt = DATETIME_EPOC + datetime.timedelta(seconds=timestamp)
|
warnings.warn(msg, DeprecationWarning)
|
||||||
return dt
|
log.warn(msg)
|
||||||
|
return util.datetime_from_timestamp(timestamp)
|
||||||
|
|
||||||
|
|
||||||
_casstypes = {}
|
_casstypes = {}
|
||||||
@@ -581,7 +585,7 @@ class DateType(_CassandraType):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def deserialize(byts, protocol_version):
|
def deserialize(byts, protocol_version):
|
||||||
timestamp = int64_unpack(byts) / 1000.0
|
timestamp = int64_unpack(byts) / 1000.0
|
||||||
return datetime_from_timestamp(timestamp)
|
return util.datetime_from_timestamp(timestamp)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def serialize(v, protocol_version):
|
def serialize(v, protocol_version):
|
||||||
@@ -652,7 +656,7 @@ class SimpleDateType(_CassandraType):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def deserialize(byts, protocol_version):
|
def deserialize(byts, protocol_version):
|
||||||
timestamp = SimpleDateType.seconds_per_day * (uint32_unpack(byts) - 2 ** 31)
|
timestamp = SimpleDateType.seconds_per_day * (uint32_unpack(byts) - 2 ** 31)
|
||||||
dt = datetime_from_timestamp(timestamp)
|
dt = util.datetime_from_timestamp(timestamp)
|
||||||
return datetime.date(dt.year, dt.month, dt.day)
|
return datetime.date(dt.year, dt.month, dt.day)
|
||||||
|
|
||||||
|
|
||||||
@@ -661,8 +665,8 @@ class TimeType(_CassandraType):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def validate(cls, val):
|
def validate(cls, val):
|
||||||
if not isinstance(val, Time):
|
if not isinstance(val, util.Time):
|
||||||
val = Time(val)
|
val = util.Time(val)
|
||||||
return val
|
return val
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -670,12 +674,12 @@ class TimeType(_CassandraType):
|
|||||||
try:
|
try:
|
||||||
nano = val.nanosecond_time
|
nano = val.nanosecond_time
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
nano = Time(val).nanosecond_time
|
nano = util.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 Time(int64_unpack(byts))
|
return util.Time(int64_unpack(byts))
|
||||||
|
|
||||||
|
|
||||||
class UTF8Type(_CassandraType):
|
class UTF8Type(_CassandraType):
|
||||||
@@ -771,7 +775,7 @@ class ListType(_SimpleParameterizedType):
|
|||||||
class SetType(_SimpleParameterizedType):
|
class SetType(_SimpleParameterizedType):
|
||||||
typename = 'set'
|
typename = 'set'
|
||||||
num_subtypes = 1
|
num_subtypes = 1
|
||||||
adapter = sortedset
|
adapter = util.sortedset
|
||||||
|
|
||||||
|
|
||||||
class MapType(_ParameterizedType):
|
class MapType(_ParameterizedType):
|
||||||
@@ -794,7 +798,7 @@ class MapType(_ParameterizedType):
|
|||||||
length = 2
|
length = 2
|
||||||
numelements = unpack(byts[:length])
|
numelements = unpack(byts[:length])
|
||||||
p = length
|
p = length
|
||||||
themap = OrderedMap()
|
themap = util.OrderedMap()
|
||||||
for _ in range(numelements):
|
for _ in range(numelements):
|
||||||
key_len = unpack(byts[p:p + length])
|
key_len = unpack(byts[p:p + length])
|
||||||
p += length
|
p += length
|
||||||
|
|||||||
@@ -1,4 +1,128 @@
|
|||||||
from __future__ import with_statement
|
from __future__ import with_statement
|
||||||
|
import calendar
|
||||||
|
import datetime
|
||||||
|
import random
|
||||||
|
import six
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
DATETIME_EPOC = datetime.datetime(1970, 1, 1)
|
||||||
|
|
||||||
|
|
||||||
|
def datetime_from_timestamp(timestamp):
|
||||||
|
"""
|
||||||
|
Creates a timezone-agnostic datetime from timestamp (in seconds) in a consistent manner.
|
||||||
|
Works around a Windows issue with large negative timestamps (PYTHON-119),
|
||||||
|
and rounding differences in Python 3.4 (PYTHON-340).
|
||||||
|
|
||||||
|
:param timestamp: a unix timestamp, in seconds
|
||||||
|
|
||||||
|
:rtype: datetime
|
||||||
|
"""
|
||||||
|
dt = DATETIME_EPOC + datetime.timedelta(seconds=timestamp)
|
||||||
|
return dt
|
||||||
|
|
||||||
|
|
||||||
|
def unix_time_from_uuid1(uuid_arg):
|
||||||
|
"""
|
||||||
|
Converts a version 1 :class:`uuid.UUID` to a timestamp with the same precision
|
||||||
|
as :meth:`time.time()` returns. This is useful for examining the
|
||||||
|
results of queries returning a v1 :class:`~uuid.UUID`.
|
||||||
|
|
||||||
|
:param uuid_arg: a version 1 :class:`~uuid.UUID`
|
||||||
|
|
||||||
|
:rtype: timestamp
|
||||||
|
|
||||||
|
"""
|
||||||
|
return (uuid_arg.time - 0x01B21DD213814000) / 10000000.0
|
||||||
|
|
||||||
|
|
||||||
|
def datetime_from_uuid1(uuid_arg):
|
||||||
|
"""
|
||||||
|
Creates a timezone-agnostic datetime from the timestamp in the
|
||||||
|
specified type-1 UUID.
|
||||||
|
|
||||||
|
:param uuid_arg: a version 1 :class:`~uuid.UUID`
|
||||||
|
|
||||||
|
:rtype: timestamp
|
||||||
|
|
||||||
|
"""
|
||||||
|
return datetime_from_timestamp(unix_time_from_uuid1(uuid_arg))
|
||||||
|
|
||||||
|
|
||||||
|
def min_uuid_from_time(timestamp):
|
||||||
|
"""
|
||||||
|
Generates the minimum TimeUUID (type 1) for a given timestamp, as compared by Cassandra.
|
||||||
|
|
||||||
|
See :func:`uuid_from_time` for argument and return types.
|
||||||
|
"""
|
||||||
|
return uuid_from_time(timestamp, 0x80, 0x808080808080) # Cassandra does byte-wise comparison; fill with min signed bytes (0x80 = -128)
|
||||||
|
|
||||||
|
|
||||||
|
def max_uuid_from_time(timestamp):
|
||||||
|
"""
|
||||||
|
Generates the maximum TimeUUID (type 1) for a given timestamp, as compared by Cassandra.
|
||||||
|
|
||||||
|
See :func:`uuid_from_time` for argument and return types.
|
||||||
|
"""
|
||||||
|
return uuid_from_time(timestamp, 0x3f7f, 0x7f7f7f7f7f7f) # Max signed bytes (0x7f = 127)
|
||||||
|
|
||||||
|
|
||||||
|
def uuid_from_time(time_arg, clock_seq=None, node=None):
|
||||||
|
"""
|
||||||
|
Converts a datetime or timestamp to a type 1 :class:`uuid.UUID`.
|
||||||
|
|
||||||
|
:param time_arg:
|
||||||
|
The time to use for the timestamp portion of the UUID.
|
||||||
|
This can either be a :class:`datetime` object or a timestamp
|
||||||
|
in seconds (as returned from :meth:`time.time()`).
|
||||||
|
:type datetime: :class:`datetime` or timestamp
|
||||||
|
|
||||||
|
:param clock_seq:
|
||||||
|
Clock sequence field for the UUID (up to 14 bits). If not specified,
|
||||||
|
a random sequence is generated.
|
||||||
|
:type clock_seq: int
|
||||||
|
|
||||||
|
:param node:
|
||||||
|
None integer for the UUID (up to 48 bits). If not specified, this
|
||||||
|
field is randomized.
|
||||||
|
:type node: long
|
||||||
|
|
||||||
|
:rtype: :class:`uuid.UUID`
|
||||||
|
|
||||||
|
"""
|
||||||
|
if hasattr(time_arg, 'utctimetuple'):
|
||||||
|
seconds = int(calendar.timegm(time_arg.utctimetuple()))
|
||||||
|
microseconds = (seconds * 1e6) + time_arg.time().microsecond
|
||||||
|
else:
|
||||||
|
microseconds = int(time_arg * 1e6)
|
||||||
|
|
||||||
|
# 0x01b21dd213814000 is the number of 100-ns intervals between the
|
||||||
|
# UUID epoch 1582-10-15 00:00:00 and the Unix epoch 1970-01-01 00:00:00.
|
||||||
|
intervals = int(microseconds * 10) + 0x01b21dd213814000
|
||||||
|
|
||||||
|
time_low = intervals & 0xffffffff
|
||||||
|
time_mid = (intervals >> 32) & 0xffff
|
||||||
|
time_hi_version = (intervals >> 48) & 0x0fff
|
||||||
|
|
||||||
|
if clock_seq is None:
|
||||||
|
clock_seq = random.getrandbits(14)
|
||||||
|
|
||||||
|
clock_seq_low = clock_seq & 0xff
|
||||||
|
clock_seq_hi_variant = 0x80 | ((clock_seq >> 8) & 0x3f)
|
||||||
|
|
||||||
|
if node is None:
|
||||||
|
node = random.getrandbits(48)
|
||||||
|
node &= 0xffffffffff
|
||||||
|
|
||||||
|
return uuid.UUID(fields=(time_low, time_mid, time_hi_version,
|
||||||
|
clock_seq_hi_variant, clock_seq_low, node), version=1)
|
||||||
|
|
||||||
|
LOWEST_TIME_UUID = uuid.UUID('00000000-0000-1000-8080-808080808080')
|
||||||
|
""" The lowest possible TimeUUID, as sorted by Cassandra. """
|
||||||
|
|
||||||
|
HIGHEST_TIME_UUID = uuid.UUID('ffffffff-ffff-1fff-bf7f-7f7f7f7f7f7f')
|
||||||
|
""" The highest possible TimeUUID, as sorted by Cassandra. """
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
@@ -557,7 +681,6 @@ except ImportError:
|
|||||||
return isect
|
return isect
|
||||||
|
|
||||||
from collections import Mapping
|
from collections import Mapping
|
||||||
import six
|
|
||||||
from six.moves import cPickle
|
from six.moves import cPickle
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user