123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 |
- # coding: utf-8
- from collections import namedtuple
- import datetime
- import sys
- import struct
- PY2 = sys.version_info[0] == 2
- if PY2:
- int_types = (int, long)
- _utc = None
- else:
- int_types = int
- try:
- _utc = datetime.timezone.utc
- except AttributeError:
- _utc = datetime.timezone(datetime.timedelta(0))
- class ExtType(namedtuple("ExtType", "code data")):
- """ExtType represents ext type in msgpack."""
- def __new__(cls, code, data):
- if not isinstance(code, int):
- raise TypeError("code must be int")
- if not isinstance(data, bytes):
- raise TypeError("data must be bytes")
- if not 0 <= code <= 127:
- raise ValueError("code must be 0~127")
- return super(ExtType, cls).__new__(cls, code, data)
- class Timestamp(object):
- """Timestamp represents the Timestamp extension type in msgpack.
- When built with Cython, msgpack uses C methods to pack and unpack `Timestamp`. When using pure-Python
- msgpack, :func:`to_bytes` and :func:`from_bytes` are used to pack and unpack `Timestamp`.
- This class is immutable: Do not override seconds and nanoseconds.
- """
- __slots__ = ["seconds", "nanoseconds"]
- def __init__(self, seconds, nanoseconds=0):
- """Initialize a Timestamp object.
- :param int seconds:
- Number of seconds since the UNIX epoch (00:00:00 UTC Jan 1 1970, minus leap seconds).
- May be negative.
- :param int nanoseconds:
- Number of nanoseconds to add to `seconds` to get fractional time.
- Maximum is 999_999_999. Default is 0.
- Note: Negative times (before the UNIX epoch) are represented as negative seconds + positive ns.
- """
- if not isinstance(seconds, int_types):
- raise TypeError("seconds must be an interger")
- if not isinstance(nanoseconds, int_types):
- raise TypeError("nanoseconds must be an integer")
- if not (0 <= nanoseconds < 10 ** 9):
- raise ValueError(
- "nanoseconds must be a non-negative integer less than 999999999."
- )
- self.seconds = seconds
- self.nanoseconds = nanoseconds
- def __repr__(self):
- """String representation of Timestamp."""
- return "Timestamp(seconds={0}, nanoseconds={1})".format(
- self.seconds, self.nanoseconds
- )
- def __eq__(self, other):
- """Check for equality with another Timestamp object"""
- if type(other) is self.__class__:
- return (
- self.seconds == other.seconds and self.nanoseconds == other.nanoseconds
- )
- return False
- def __ne__(self, other):
- """not-equals method (see :func:`__eq__()`)"""
- return not self.__eq__(other)
- def __hash__(self):
- return hash((self.seconds, self.nanoseconds))
- @staticmethod
- def from_bytes(b):
- """Unpack bytes into a `Timestamp` object.
- Used for pure-Python msgpack unpacking.
- :param b: Payload from msgpack ext message with code -1
- :type b: bytes
- :returns: Timestamp object unpacked from msgpack ext payload
- :rtype: Timestamp
- """
- if len(b) == 4:
- seconds = struct.unpack("!L", b)[0]
- nanoseconds = 0
- elif len(b) == 8:
- data64 = struct.unpack("!Q", b)[0]
- seconds = data64 & 0x00000003FFFFFFFF
- nanoseconds = data64 >> 34
- elif len(b) == 12:
- nanoseconds, seconds = struct.unpack("!Iq", b)
- else:
- raise ValueError(
- "Timestamp type can only be created from 32, 64, or 96-bit byte objects"
- )
- return Timestamp(seconds, nanoseconds)
- def to_bytes(self):
- """Pack this Timestamp object into bytes.
- Used for pure-Python msgpack packing.
- :returns data: Payload for EXT message with code -1 (timestamp type)
- :rtype: bytes
- """
- if (self.seconds >> 34) == 0: # seconds is non-negative and fits in 34 bits
- data64 = self.nanoseconds << 34 | self.seconds
- if data64 & 0xFFFFFFFF00000000 == 0:
- # nanoseconds is zero and seconds < 2**32, so timestamp 32
- data = struct.pack("!L", data64)
- else:
- # timestamp 64
- data = struct.pack("!Q", data64)
- else:
- # timestamp 96
- data = struct.pack("!Iq", self.nanoseconds, self.seconds)
- return data
- @staticmethod
- def from_unix(unix_sec):
- """Create a Timestamp from posix timestamp in seconds.
- :param unix_float: Posix timestamp in seconds.
- :type unix_float: int or float.
- """
- seconds = int(unix_sec // 1)
- nanoseconds = int((unix_sec % 1) * 10 ** 9)
- return Timestamp(seconds, nanoseconds)
- def to_unix(self):
- """Get the timestamp as a floating-point value.
- :returns: posix timestamp
- :rtype: float
- """
- return self.seconds + self.nanoseconds / 1e9
- @staticmethod
- def from_unix_nano(unix_ns):
- """Create a Timestamp from posix timestamp in nanoseconds.
- :param int unix_ns: Posix timestamp in nanoseconds.
- :rtype: Timestamp
- """
- return Timestamp(*divmod(unix_ns, 10 ** 9))
- def to_unix_nano(self):
- """Get the timestamp as a unixtime in nanoseconds.
- :returns: posix timestamp in nanoseconds
- :rtype: int
- """
- return self.seconds * 10 ** 9 + self.nanoseconds
- def to_datetime(self):
- """Get the timestamp as a UTC datetime.
- Python 2 is not supported.
- :rtype: datetime.
- """
- return datetime.datetime.fromtimestamp(self.to_unix(), _utc)
- @staticmethod
- def from_datetime(dt):
- """Create a Timestamp from datetime with tzinfo.
- Python 2 is not supported.
- :rtype: Timestamp
- """
- return Timestamp.from_unix(dt.timestamp())
|