3636import re
3737import socket
3838import time
39- from datetime import datetime , timedelta
39+ import datetime
4040from uuid import UUID
4141import warnings
4242
5555
5656if six .PY3 :
5757 _number_types = frozenset ((int , float ))
58- _time_types = frozenset ((int ))
59- _date_types = frozenset ((int ))
58+ _time_types = frozenset ((int , ))
59+ _date_types = frozenset ((int , ))
6060 long = int
6161else :
6262 _number_types = frozenset ((int , long , float ))
@@ -74,6 +74,15 @@ def unix_time_from_uuid1(u):
7474 return (u .time - 0x01B21DD213814000 ) / 10000000.0
7575
7676
77+ def datetime_from_timestamp (timestamp ):
78+ if timestamp >= 0 :
79+ dt = datetime .datetime .utcfromtimestamp (timestamp )
80+ else :
81+ # PYTHON-119: workaround for Windows
82+ dt = datetime .datetime (1970 , 1 , 1 ) + datetime .timedelta (seconds = timestamp )
83+ return dt
84+
85+
7786_casstypes = {}
7887
7988
@@ -543,39 +552,34 @@ class DateType(_CassandraType):
543552 typename = 'timestamp'
544553
545554 @classmethod
546- def validate (cls , date ):
547- if isinstance (date , six .string_types ):
548- date = cls .interpret_datestring (date )
549- return date
555+ def validate (cls , val ):
556+ if isinstance (val , six .string_types ):
557+ val = cls .interpret_datestring (val )
558+ return val
550559
551560 @staticmethod
552- def interpret_datestring (date ):
553- if date [- 5 ] in ('+' , '-' ):
554- offset = (int (date [- 4 :- 2 ]) * 3600 + int (date [- 2 :]) * 60 ) * int (date [- 5 ] + '1' )
555- date = date [:- 5 ]
561+ def interpret_datestring (val ):
562+ if val [- 5 ] in ('+' , '-' ):
563+ offset = (int (val [- 4 :- 2 ]) * 3600 + int (val [- 2 :]) * 60 ) * int (val [- 5 ] + '1' )
564+ val = val [:- 5 ]
556565 else :
557566 offset = - time .timezone
558567 for tformat in cql_timestamp_formats :
559568 try :
560- tval = time .strptime (date , tformat )
569+ tval = time .strptime (val , tformat )
561570 except ValueError :
562571 continue
563572 return calendar .timegm (tval ) + offset
564573 else :
565- raise ValueError ("can't interpret %r as a date" % (date ,))
574+ raise ValueError ("can't interpret %r as a date" % (val ,))
566575
567576 def my_timestamp (self ):
568577 return self .val
569578
570579 @staticmethod
571580 def deserialize (byts , protocol_version ):
572581 timestamp = int64_unpack (byts ) / 1000.0
573- if timestamp >= 0 :
574- dt = datetime .utcfromtimestamp (timestamp )
575- else :
576- # PYTHON-119: workaround for Windows
577- dt = datetime (1970 , 1 , 1 ) + timedelta (seconds = timestamp )
578- return dt
582+ return datetime_from_timestamp (timestamp )
579583
580584 @staticmethod
581585 def serialize (v , protocol_version ):
@@ -635,85 +639,87 @@ class SimpleDateType(_CassandraType):
635639 date_format = "%Y-%m-%d"
636640
637641 @classmethod
638- def validate (cls , date ):
639- if isinstance (date , basestring ):
640- date = cls .interpret_simpledate_string (date )
641- return date
642+ def validate (cls , val ):
643+ if isinstance (val , six .string_types ):
644+ val = cls .interpret_simpledate_string (val )
645+ elif (not isinstance (val , datetime .date )) and (type (val ) not in _date_types ):
646+ raise TypeError ('SimpleDateType arg must be a datetime.date, unsigned integer, or string in the format YYYY-MM-DD' )
647+ return val
642648
643649 @staticmethod
644650 def interpret_simpledate_string (v ):
645- try :
646- tval = time .strptime (v , SimpleDateType .date_format )
647- # shift upward w/epoch at 2**31
648- return (calendar .timegm (tval ) / SimpleDateType .seconds_per_day ) + 2 ** 31
649- except TypeError :
650- # Ints are valid dates too
651- if type (v ) not in _date_types :
652- raise TypeError ('Date arguments must be an unsigned integer or string in the format YYYY-MM-DD' )
653- return v
651+ date_time = datetime .datetime .strptime (v , SimpleDateType .date_format )
652+ return datetime .date (date_time .year , date_time .month , date_time .day )
654653
655654 @staticmethod
656655 def serialize (val , protocol_version ):
657- date_val = SimpleDateType .interpret_simpledate_string (val )
658- return uint32_pack (date_val )
656+ # Values of the 'date'` type are encoded as 32-bit unsigned integers
657+ # representing a number of days with "the epoch" at the center of the
658+ # range (2^31). Epoch is January 1st, 1970
659+ try :
660+ shifted = (calendar .timegm (val .timetuple ()) // SimpleDateType .seconds_per_day ) + 2 ** 31
661+ except AttributeError :
662+ shifted = val
663+ return uint32_pack (shifted )
659664
660665 @staticmethod
661666 def deserialize (byts , protocol_version ):
662- Result = namedtuple ('SimpleDate' , 'value' )
663- return Result (value = uint32_unpack (byts ))
667+ timestamp = SimpleDateType .seconds_per_day * (uint32_unpack (byts ) - 2 ** 31 )
668+ dt = datetime .datetime .utcfromtimestamp (timestamp )
669+ return datetime .date (dt .year , dt .month , dt .day )
664670
665671
666672class TimeType (_CassandraType ):
667673 typename = 'time'
668- ONE_MICRO = 1000
669- ONE_MILLI = 1000 * ONE_MICRO
670- ONE_SECOND = 1000 * ONE_MILLI
671- ONE_MINUTE = 60 * ONE_SECOND
672- ONE_HOUR = 60 * ONE_MINUTE
674+ ONE_MICRO = 1000
675+ ONE_MILLI = 1000 * ONE_MICRO
676+ ONE_SECOND = 1000 * ONE_MILLI
677+ ONE_MINUTE = 60 * ONE_SECOND
678+ ONE_HOUR = 60 * ONE_MINUTE
673679
674680 @classmethod
675681 def validate (cls , val ):
676- if isinstance (val , basestring ):
677- time = cls .interpret_timestring (val )
678- return time
682+ if isinstance (val , six .string_types ):
683+ val = cls .interpret_timestring (val )
684+ elif (not isinstance (val , datetime .time )) and (type (val ) not in _time_types ):
685+ raise TypeError ('TimeType arguments must be a string or whole number' )
686+ return val
679687
680688 @staticmethod
681689 def interpret_timestring (val ):
682690 try :
683691 nano = 0
684- try :
685- base_time_str = val
686- if '.' in base_time_str :
687- base_time_str = val [0 :val .find ('.' )]
688- base_time = time .strptime (base_time_str , "%H:%M:%S" )
689- nano = base_time .tm_hour * TimeType .ONE_HOUR
690- nano += base_time .tm_min * TimeType .ONE_MINUTE
691- nano += base_time .tm_sec * TimeType .ONE_SECOND
692-
693- if '.' in val :
694- nano_time_str = val [val .find ('.' )+ 1 :]
695- # right pad to 9 digits
696- while len (nano_time_str ) < 9 :
697- nano_time_str += "0"
698- nano += int (nano_time_str )
699-
700- except AttributeError as e :
701- if type (val ) not in _time_types :
702- raise TypeError ('TimeType arguments must be a string or whole number' )
703- # long / int values passed in are acceptable too
704- nano = val
692+ parts = val .split ('.' )
693+ base_time = time .strptime (parts [0 ], "%H:%M:%S" )
694+ nano = (base_time .tm_hour * TimeType .ONE_HOUR +
695+ base_time .tm_min * TimeType .ONE_MINUTE +
696+ base_time .tm_sec * TimeType .ONE_SECOND )
697+
698+ if len (parts ) > 1 :
699+ # right pad to 9 digits
700+ nano_time_str = parts [1 ] + "0" * (9 - len (parts [1 ]))
701+ nano += int (nano_time_str )
702+
705703 return nano
706- except ValueError as e :
704+ except ValueError :
707705 raise ValueError ("can't interpret %r as a time" % (val ,))
708706
709707 @staticmethod
710708 def serialize (val , protocol_version ):
711- return int64_pack (TimeType .interpret_timestring (val ))
709+ # Values of the @time@ type are encoded as 64-bit signed integers
710+ # representing the number of nanoseconds since midnight.
711+ try :
712+ nano = (val .hour * TimeType .ONE_HOUR +
713+ val .minute * TimeType .ONE_MINUTE +
714+ val .second * TimeType .ONE_SECOND +
715+ val .microsecond * TimeType .ONE_MICRO )
716+ except AttributeError :
717+ nano = val
718+ return int64_pack (nano )
712719
713720 @staticmethod
714721 def deserialize (byts , protocol_version ):
715- Result = namedtuple ('Time' , 'value' )
716- return Result (value = int64_unpack (byts ))
722+ return int64_unpack (byts )
717723
718724
719725class UTF8Type (_CassandraType ):
0 commit comments