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:
Adam Holmberg
2015-03-09 11:38:19 -05:00
parent f7e014a1fb
commit 8d40400beb
2 changed files with 147 additions and 20 deletions

View File

@@ -34,24 +34,27 @@ from collections import namedtuple
import datetime
from decimal import Decimal
import io
import logging
import re
import socket
import time
import sys
from uuid import UUID
import six
from six.moves import range
import sys
from uuid import UUID
import warnings
from cassandra.marshal import (int8_pack, int8_unpack,
uint16_pack, uint16_unpack, uint32_pack, uint32_unpack,
int32_pack, int32_unpack, int64_pack, int64_unpack,
float_pack, float_unpack, double_pack, double_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.'
log = logging.getLogger(__name__)
if six.PY3:
_number_types = frozenset((int, float))
@@ -72,16 +75,17 @@ def trim_if_startswith(s, prefix):
def unix_time_from_uuid1(u):
return (u.time - 0x01B21DD213814000) / 10000000.0
DATETIME_EPOC = datetime.datetime(1970, 1, 1)
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)
return util.unix_time_from_uuid1(u)
def datetime_from_timestamp(timestamp):
# PYTHON-119: workaround for Windows
dt = DATETIME_EPOC + datetime.timedelta(seconds=timestamp)
return dt
msg = "'cassandra.cqltypes.datetime_from_timestamp' has moved to 'cassandra.util'. This entry point will be removed in the next major version."
warnings.warn(msg, DeprecationWarning)
log.warn(msg)
return util.datetime_from_timestamp(timestamp)
_casstypes = {}
@@ -581,7 +585,7 @@ class DateType(_CassandraType):
@staticmethod
def deserialize(byts, protocol_version):
timestamp = int64_unpack(byts) / 1000.0
return datetime_from_timestamp(timestamp)
return util.datetime_from_timestamp(timestamp)
@staticmethod
def serialize(v, protocol_version):
@@ -652,7 +656,7 @@ class SimpleDateType(_CassandraType):
@staticmethod
def deserialize(byts, protocol_version):
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)
@@ -661,8 +665,8 @@ class TimeType(_CassandraType):
@classmethod
def validate(cls, val):
if not isinstance(val, Time):
val = Time(val)
if not isinstance(val, util.Time):
val = util.Time(val)
return val
@staticmethod
@@ -670,12 +674,12 @@ class TimeType(_CassandraType):
try:
nano = val.nanosecond_time
except AttributeError:
nano = Time(val).nanosecond_time
nano = util.Time(val).nanosecond_time
return int64_pack(nano)
@staticmethod
def deserialize(byts, protocol_version):
return Time(int64_unpack(byts))
return util.Time(int64_unpack(byts))
class UTF8Type(_CassandraType):
@@ -771,7 +775,7 @@ class ListType(_SimpleParameterizedType):
class SetType(_SimpleParameterizedType):
typename = 'set'
num_subtypes = 1
adapter = sortedset
adapter = util.sortedset
class MapType(_ParameterizedType):
@@ -794,7 +798,7 @@ class MapType(_ParameterizedType):
length = 2
numelements = unpack(byts[:length])
p = length
themap = OrderedMap()
themap = util.OrderedMap()
for _ in range(numelements):
key_len = unpack(byts[p:p + length])
p += length

View File

@@ -1,4 +1,128 @@
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:
from collections import OrderedDict
@@ -557,7 +681,6 @@ except ImportError:
return isect
from collections import Mapping
import six
from six.moves import cPickle