summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Fischer <yanlend@users.noreply.github.com>2020-07-30 16:48:51 +0200
committerGitHub <noreply@github.com>2020-07-30 23:48:51 +0900
commit8fb709f2e0438862020d8810fa70a81fb5dac7d4 (patch)
tree69c53d33afbc788149b99fe89a7784d7a85e79a6
parent772c830841a276adb392dd449809764b2826b1f8 (diff)
downloadmsgpack-python-8fb709f2e0438862020d8810fa70a81fb5dac7d4.tar.gz
Fix datetime before epoch on windows in cython implementation (#436)
Cython implementation still used datetime.from_timestamp method, which does not work on windows. Update the cython implementation to use utc time and delta and add a regression test to highlight the issue.
-rw-r--r--Makefile1
-rw-r--r--msgpack/unpack.h34
-rw-r--r--test/test_timestamp.py8
3 files changed, 30 insertions, 13 deletions
diff --git a/Makefile b/Makefile
index 2a4c0af..0110ddf 100644
--- a/Makefile
+++ b/Makefile
@@ -25,6 +25,7 @@ clean:
rm -rf build
rm -f msgpack/_cmsgpack.cpp
rm -f msgpack/_cmsgpack.*.so
+ rm -f msgpack/_cmsgpack.*.pyd
rm -rf msgpack/__pycache__
rm -rf test/__pycache__
diff --git a/msgpack/unpack.h b/msgpack/unpack.h
index debdf71..868b96e 100644
--- a/msgpack/unpack.h
+++ b/msgpack/unpack.h
@@ -341,7 +341,26 @@ static int unpack_callback_ext(unpack_user* u, const char* base, const char* pos
else if (u->timestamp == 0) { // Timestamp
py = PyObject_CallFunction(u->timestamp_t, "(Lk)", ts.tv_sec, ts.tv_nsec);
}
- else { // float or datetime
+ else if (u->timestamp == 3) { // datetime
+ // Calculate datetime using epoch + delta
+ // due to limitations PyDateTime_FromTimestamp on Windows with negative timestamps
+ PyObject *epoch = PyDateTimeAPI->DateTime_FromDateAndTime(1970, 1, 1, 0, 0, 0, 0, u->utc, PyDateTimeAPI->DateTimeType);
+ if (epoch == NULL) {
+ return -1;
+ }
+
+ PyObject* d = PyDelta_FromDSU(0, ts.tv_sec, ts.tv_nsec / 1000);
+ if (d == NULL) {
+ Py_DECREF(epoch);
+ return -1;
+ }
+
+ py = PyNumber_Add(epoch, d);
+
+ Py_DECREF(epoch);
+ Py_DECREF(d);
+ }
+ else { // float
PyObject *a = PyFloat_FromDouble((double)ts.tv_nsec);
if (a == NULL) return -1;
@@ -358,18 +377,7 @@ static int unpack_callback_ext(unpack_user* u, const char* base, const char* pos
a = PyNumber_Add(b, c);
Py_DECREF(b);
Py_DECREF(c);
-
- if (u->timestamp == 3) { // datetime
- PyObject *t = PyTuple_Pack(2, a, u->utc);
- Py_DECREF(a);
- if (t == NULL) {
- return -1;
- }
- py = PyDateTime_FromTimestamp(t);
- Py_DECREF(t);
- } else { // float
- py = a;
- }
+ py = a;
}
} else {
py = PyObject_CallFunction(u->ext_hook, "(iy#)", (int)typecode, pos, (Py_ssize_t)length-1);
diff --git a/test/test_timestamp.py b/test/test_timestamp.py
index 823fe04..edc488a 100644
--- a/test/test_timestamp.py
+++ b/test/test_timestamp.py
@@ -100,6 +100,14 @@ def test_unpack_datetime():
@pytest.mark.skipif(sys.version_info[0] == 2, reason="datetime support is PY3+ only")
+def test_pack_unpack_before_epoch():
+ t_in = datetime.datetime(1960, 1, 1, tzinfo=_utc)
+ packed = msgpack.packb(t_in, datetime=True)
+ unpacked = msgpack.unpackb(packed, timestamp=3)
+ assert unpacked == t_in
+
+
+@pytest.mark.skipif(sys.version_info[0] == 2, reason="datetime support is PY3+ only")
def test_pack_datetime():
t = Timestamp(42, 14000)
dt = t.to_datetime()