| 
									
										
										
										
											2019-12-11 23:48:16 +09:00
										 |  |  | import datetime | 
					
						
							| 
									
										
										
										
											2019-12-05 18:29:15 +09:00
										 |  |  | import struct | 
					
						
							| 
									
										
										
										
											2024-05-06 11:46:31 +09:00
										 |  |  | from collections import namedtuple | 
					
						
							| 
									
										
										
										
											2019-12-05 18:29:15 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-05 18:50:13 +09:00
										 |  |  | class ExtType(namedtuple("ExtType", "code data")): | 
					
						
							| 
									
										
										
										
											2019-12-05 18:29:15 +09:00
										 |  |  |     """ExtType represents ext type in msgpack.""" | 
					
						
							| 
									
										
										
										
											2019-12-05 18:50:13 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-05 18:29:15 +09:00
										 |  |  |     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") | 
					
						
							| 
									
										
										
										
											2023-05-23 18:41:08 +02:00
										 |  |  |         return super().__new__(cls, code, data) | 
					
						
							| 
									
										
										
										
											2019-12-05 18:29:15 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-23 18:41:08 +02:00
										 |  |  | class Timestamp: | 
					
						
							| 
									
										
										
										
											2019-12-05 18:29:15 +09:00
										 |  |  |     """Timestamp represents the Timestamp extension type in msgpack.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-05 03:51:04 +02:00
										 |  |  |     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`. | 
					
						
							| 
									
										
										
										
											2019-12-12 19:59:06 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  |     This class is immutable: Do not override seconds and nanoseconds. | 
					
						
							| 
									
										
										
										
											2019-12-05 18:29:15 +09:00
										 |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2019-12-05 18:50:13 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-05 18:29:15 +09:00
										 |  |  |     __slots__ = ["seconds", "nanoseconds"] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, seconds, nanoseconds=0): | 
					
						
							|  |  |  |         """Initialize a Timestamp object.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-12 19:43:59 +09:00
										 |  |  |         :param int seconds: | 
					
						
							|  |  |  |             Number of seconds since the UNIX epoch (00:00:00 UTC Jan 1 1970, minus leap seconds). | 
					
						
							|  |  |  |             May be negative. | 
					
						
							| 
									
										
										
										
											2019-12-05 18:29:15 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-12 19:43:59 +09:00
										 |  |  |         :param int nanoseconds: | 
					
						
							|  |  |  |             Number of nanoseconds to add to `seconds` to get fractional time. | 
					
						
							|  |  |  |             Maximum is 999_999_999.  Default is 0. | 
					
						
							| 
									
										
										
										
											2019-12-05 18:29:15 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-05 03:51:04 +02:00
										 |  |  |         Note: Negative times (before the UNIX epoch) are represented as neg. seconds + pos. ns. | 
					
						
							| 
									
										
										
										
											2019-12-05 18:29:15 +09:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2023-05-21 09:26:39 +02:00
										 |  |  |         if not isinstance(seconds, int): | 
					
						
							| 
									
										
										
										
											2023-03-05 15:45:38 +01:00
										 |  |  |             raise TypeError("seconds must be an integer") | 
					
						
							| 
									
										
										
										
											2023-05-21 09:26:39 +02:00
										 |  |  |         if not isinstance(nanoseconds, int): | 
					
						
							| 
									
										
										
										
											2019-12-05 18:29:15 +09:00
										 |  |  |             raise TypeError("nanoseconds must be an integer") | 
					
						
							| 
									
										
										
										
											2022-03-03 12:29:55 +09:00
										 |  |  |         if not (0 <= nanoseconds < 10**9): | 
					
						
							| 
									
										
										
										
											2023-05-23 18:41:08 +02:00
										 |  |  |             raise ValueError("nanoseconds must be a non-negative integer less than 999999999.") | 
					
						
							| 
									
										
										
										
											2019-12-12 19:43:59 +09:00
										 |  |  |         self.seconds = seconds | 
					
						
							|  |  |  |         self.nanoseconds = nanoseconds | 
					
						
							| 
									
										
										
										
											2019-12-05 18:29:15 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __repr__(self): | 
					
						
							|  |  |  |         """String representation of Timestamp.""" | 
					
						
							| 
									
										
										
										
											2023-05-23 18:41:08 +02:00
										 |  |  |         return f"Timestamp(seconds={self.seconds}, nanoseconds={self.nanoseconds})" | 
					
						
							| 
									
										
										
										
											2019-12-05 18:29:15 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __eq__(self, other): | 
					
						
							|  |  |  |         """Check for equality with another Timestamp object""" | 
					
						
							|  |  |  |         if type(other) is self.__class__: | 
					
						
							| 
									
										
										
										
											2023-05-23 18:41:08 +02:00
										 |  |  |             return self.seconds == other.seconds and self.nanoseconds == other.nanoseconds | 
					
						
							| 
									
										
										
										
											2019-12-05 18:29:15 +09:00
										 |  |  |         return False | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-12 20:05:25 +09:00
										 |  |  |     def __ne__(self, other): | 
					
						
							|  |  |  |         """not-equals method (see :func:`__eq__()`)""" | 
					
						
							|  |  |  |         return not self.__eq__(other) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-12 19:59:06 +09:00
										 |  |  |     def __hash__(self): | 
					
						
							|  |  |  |         return hash((self.seconds, self.nanoseconds)) | 
					
						
							| 
									
										
										
										
											2019-12-05 18:29:15 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  |     @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] | 
					
						
							| 
									
										
										
										
											2019-12-05 18:50:13 +09:00
										 |  |  |             seconds = data64 & 0x00000003FFFFFFFF | 
					
						
							| 
									
										
										
										
											2019-12-05 18:29:15 +09:00
										 |  |  |             nanoseconds = data64 >> 34 | 
					
						
							|  |  |  |         elif len(b) == 12: | 
					
						
							|  |  |  |             nanoseconds, seconds = struct.unpack("!Iq", b) | 
					
						
							|  |  |  |         else: | 
					
						
							| 
									
										
										
										
											2019-12-05 18:50:13 +09:00
										 |  |  |             raise ValueError( | 
					
						
							|  |  |  |                 "Timestamp type can only be created from 32, 64, or 96-bit byte objects" | 
					
						
							|  |  |  |             ) | 
					
						
							| 
									
										
										
										
											2019-12-05 18:29:15 +09:00
										 |  |  |         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 | 
					
						
							| 
									
										
										
										
											2019-12-05 18:50:13 +09:00
										 |  |  |             if data64 & 0xFFFFFFFF00000000 == 0: | 
					
						
							| 
									
										
										
										
											2019-12-05 18:29:15 +09:00
										 |  |  |                 # 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 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-12 19:43:59 +09:00
										 |  |  |     @staticmethod | 
					
						
							|  |  |  |     def from_unix(unix_sec): | 
					
						
							|  |  |  |         """Create a Timestamp from posix timestamp in seconds.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         :param unix_float: Posix timestamp in seconds. | 
					
						
							| 
									
										
										
										
											2023-08-31 05:56:24 +02:00
										 |  |  |         :type unix_float: int or float | 
					
						
							| 
									
										
										
										
											2019-12-12 19:43:59 +09:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         seconds = int(unix_sec // 1) | 
					
						
							| 
									
										
										
										
											2022-03-03 12:29:55 +09:00
										 |  |  |         nanoseconds = int((unix_sec % 1) * 10**9) | 
					
						
							| 
									
										
										
										
											2019-12-12 19:43:59 +09:00
										 |  |  |         return Timestamp(seconds, nanoseconds) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def to_unix(self): | 
					
						
							| 
									
										
										
										
											2019-12-05 18:29:15 +09:00
										 |  |  |         """Get the timestamp as a floating-point value.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         :returns: posix timestamp | 
					
						
							|  |  |  |         :rtype: float | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2019-12-05 18:50:13 +09:00
										 |  |  |         return self.seconds + self.nanoseconds / 1e9 | 
					
						
							| 
									
										
										
										
											2019-12-05 18:29:15 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-11 23:48:16 +09:00
										 |  |  |     @staticmethod | 
					
						
							| 
									
										
										
										
											2019-12-12 19:43:59 +09:00
										 |  |  |     def from_unix_nano(unix_ns): | 
					
						
							|  |  |  |         """Create a Timestamp from posix timestamp in nanoseconds.
 | 
					
						
							| 
									
										
										
										
											2019-12-11 23:48:16 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-12 19:43:59 +09:00
										 |  |  |         :param int unix_ns: Posix timestamp in nanoseconds. | 
					
						
							|  |  |  |         :rtype: Timestamp | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2022-03-03 12:29:55 +09:00
										 |  |  |         return Timestamp(*divmod(unix_ns, 10**9)) | 
					
						
							| 
									
										
										
										
											2019-12-12 19:43:59 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def to_unix_nano(self): | 
					
						
							| 
									
										
										
										
											2019-12-05 18:29:15 +09:00
										 |  |  |         """Get the timestamp as a unixtime in nanoseconds.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         :returns: posix timestamp in nanoseconds | 
					
						
							|  |  |  |         :rtype: int | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2022-03-03 12:29:55 +09:00
										 |  |  |         return self.seconds * 10**9 + self.nanoseconds | 
					
						
							| 
									
										
										
										
											2019-12-11 23:48:16 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-12 19:43:59 +09:00
										 |  |  |     def to_datetime(self): | 
					
						
							|  |  |  |         """Get the timestamp as a UTC datetime.
 | 
					
						
							| 
									
										
										
										
											2019-12-11 23:48:16 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-31 05:56:24 +02:00
										 |  |  |         :rtype: `datetime.datetime` | 
					
						
							| 
									
										
										
										
											2019-12-12 19:43:59 +09:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2023-05-21 09:26:39 +02:00
										 |  |  |         utc = datetime.timezone.utc | 
					
						
							| 
									
										
										
										
											2024-04-20 00:46:30 +02:00
										 |  |  |         return datetime.datetime.fromtimestamp(0, utc) + datetime.timedelta( | 
					
						
							|  |  |  |             seconds=self.seconds, microseconds=self.nanoseconds // 1000 | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2019-12-11 23:48:16 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-12 19:43:59 +09:00
										 |  |  |     @staticmethod | 
					
						
							|  |  |  |     def from_datetime(dt): | 
					
						
							|  |  |  |         """Create a Timestamp from datetime with tzinfo.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         :rtype: Timestamp | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2024-04-20 00:46:30 +02:00
										 |  |  |         return Timestamp(seconds=int(dt.timestamp()), nanoseconds=dt.microsecond * 1000) |