Skip to content

Commit 2e695b7

Browse files
msgpack: support tzoffset in datetime
Support non-zero tzoffset in datetime extended type. If tzoffset and tzindex are not specified, return timezone-naive pandas.Timestamp. If tzoffset is specified, return timezone-aware pandas.Timestamp with pytz.FixedOffset timezone info. Part of #204
1 parent 77761ca commit 2e695b7

File tree

2 files changed

+49
-4
lines changed

2 files changed

+49
-4
lines changed

tarantool/msgpack_ext_types/datetime.py

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import time
22
import math
33
import pandas
4+
import pytz
45

56
# https://www.tarantool.io/ru/doc/latest/dev_guide/internals/msgpack_extensions/#the-datetime-type
67
#
@@ -45,15 +46,30 @@
4546
NSEC_IN_SEC = 1000000000
4647
assert isinstance(NSEC_IN_SEC, int)
4748

49+
SEC_IN_MIN = 60
50+
assert isinstance(SEC_IN_MIN, int)
51+
52+
MIN_IN_DAY = 60 * 24
53+
assert isinstance(MIN_IN_DAY, int)
54+
4855
def get_int_as_bytes(data, size):
4956
return data.to_bytes(size, byteorder=BYTEORDER, signed=True)
5057

5158
def encode(obj):
5259
seconds = obj.value // NSEC_IN_SEC
5360
nsec = obj.value % NSEC_IN_SEC
61+
5462
tzoffset = 0
5563
tzindex = 0
5664

65+
if obj.tz is not None:
66+
if obj.tz.zone is not None:
67+
raise NotImplementedError
68+
else:
69+
utc_offset = obj.tz.utcoffset(0)
70+
# There is no precision loss since pytz.FixedOffset is in minutes
71+
tzoffset = utc_offset.days * MIN_IN_DAY + utc_offset.seconds // SEC_IN_MIN
72+
5773
bytes_buffer = get_int_as_bytes(seconds, SECONDS_SIZE_BYTES)
5874

5975
if (nsec != 0) or (tzoffset != 0) or (tzindex != 0):
@@ -80,9 +96,14 @@ def decode(data):
8096
tzoffset = 0
8197
tzindex = 0
8298

83-
if (tzoffset != 0) or (tzindex != 0):
84-
raise NotImplementedError
85-
8699
total_nsec = seconds * NSEC_IN_SEC + nsec
87100

88-
return pandas.to_datetime(total_nsec, unit='ns')
101+
tzinfo = None
102+
if (tzindex != 0):
103+
raise NotImplementedError
104+
elif (tzoffset != 0):
105+
tzinfo = pytz.FixedOffset(tzoffset)
106+
return pandas.to_datetime(total_nsec, unit='ns').replace(tzinfo=pytz.utc).tz_convert(tzinfo)
107+
else:
108+
# return timezone-naive pandas.Timestamp
109+
return pandas.to_datetime(total_nsec, unit='ns')

test/suites/test_msgpack_ext_types.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import warnings
1111
import tarantool
1212
import pandas
13+
import pytz
1314

1415
from tarantool.msgpack_ext_types.packer import default as packer_default
1516
from tarantool.msgpack_ext_types.unpacker import ext_hook as unpacker_ext_hook
@@ -544,6 +545,29 @@ def test_UUID_tarantool_encode(self):
544545
'tarantool': r"datetime.new({year=2022, month=8, day=31, hour=18, min=7, sec=54, " +
545546
r"nsec=308543321})",
546547
},
548+
{
549+
'python': pandas.Timestamp(year=2022, month=8, day=31, hour=18, minute=7, second=54,
550+
microsecond=308543, nanosecond=321),
551+
'msgpack': (b'\x7a\xa3\x0f\x63\x00\x00\x00\x00\x59\xff\x63\x12\x00\x00\x00\x00'),
552+
'tarantool': r"datetime.new({year=2022, month=8, day=31, hour=18, min=7, sec=54, " +
553+
r"nsec=308543321})",
554+
},
555+
{
556+
'python': pandas.Timestamp(year=2022, month=8, day=31, hour=18, minute=7, second=54,
557+
microsecond=308543, nanosecond=321,
558+
tzinfo=pytz.FixedOffset(180)),
559+
'msgpack': (b'\x4a\x79\x0f\x63\x00\x00\x00\x00\x59\xff\x63\x12\xb4\x00\x00\x00'),
560+
'tarantool': r"datetime.new({year=2022, month=8, day=31, hour=18, min=7, sec=54, " +
561+
r"nsec=308543321, tzoffset=180})",
562+
},
563+
{
564+
'python': pandas.Timestamp(year=2022, month=8, day=31, hour=18, minute=7, second=54,
565+
microsecond=308543, nanosecond=321,
566+
tzinfo=pytz.FixedOffset(-60)),
567+
'msgpack': (b'\x8a\xb1\x0f\x63\x00\x00\x00\x00\x59\xff\x63\x12\xc4\xff\x00\x00'),
568+
'tarantool': r"datetime.new({year=2022, month=8, day=31, hour=18, min=7, sec=54, " +
569+
r"nsec=308543321, tzoffset=-60})",
570+
},
547571
]
548572

549573
def test_datetime_msgpack_decode(self):

0 commit comments

Comments
 (0)